diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..993e9007d6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,500 @@ +name: Build +on: + workflow_call: + inputs: + signed-ios: + description: Build and export a signed iOS IPA + required: false + type: boolean + default: false + +env: + SQLITE3_RELEASE_TAG: ${{ vars.SQLITE3_RELEASE_TAG || 'sqlite-3.32.3' }} + SQLITE3_GITHUB_REPO: ${{ secrets.SQLITE3_GITHUB_REPO || 'TotalCross/totalcross-sqlite3-build' }} + SQLITE3_GITHUB_TOKEN: ${{ secrets.SQLITE3_GITHUB_TOKEN }} + ZLIB_NG_RELEASE_TAG: ${{ vars.ZLIB_NG_RELEASE_TAG || 'zlib-ng-2.1.6' }} + ZLIB_NG_GITHUB_REPO: ${{ secrets.ZLIB_NG_GITHUB_REPO || 'TotalCross/totalcross-depot-tools' }} + ZLIB_NG_GITHUB_TOKEN: ${{ secrets.ZLIB_NG_GITHUB_TOKEN }} + LIBPNG_RELEASE_TAG: ${{ vars.LIBPNG_RELEASE_TAG || 'libpng-1.6.48' }} + LIBPNG_GITHUB_REPO: ${{ secrets.LIBPNG_GITHUB_REPO || 'TotalCross/totalcross-depot-tools' }} + LIBPNG_GITHUB_TOKEN: ${{ secrets.LIBPNG_GITHUB_TOKEN }} + +jobs: + checkout-source: + name: Checkout source code + runs-on: ubuntu-22.04 + + steps: + - name: Checkout source code + uses: actions/checkout@v6 + + - name: Pack source code + run: tar --exclude=.git -czf "${RUNNER_TEMP}/source-code.tar.gz" . + + - name: Upload source code + uses: actions/upload-artifact@v7 + with: + name: source-code + path: ${{ runner.temp }}/source-code.tar.gz + retention-days: 1 + + build-linux: + name: Build '${{ matrix.project }}' + needs: checkout-source + strategy: + fail-fast: false + matrix: + include: + - project: linux-amd64 + runner: ubuntu-22.04 + docker_platform: linux/amd64 + use_qemu: false + - project: linux-arm32v7 + runner: ubuntu-22.04 + docker_platform: linux/arm/v7 + use_qemu: true + - project: linux-arm64 + runner: ubuntu-22.04-arm + docker_platform: linux/arm64 + use_qemu: false + + runs-on: ${{ matrix.runner }} + + steps: + - name: Download source code + uses: actions/download-artifact@v7 + with: + name: source-code + + - name: Extract source code + run: tar -xzf source-code.tar.gz + + - name: Cache artifacts + uses: actions/cache@v5 + with: + path: | + TotalCrossVM/deps/totalcross-depot-tools/skia/local + TotalCrossVM/deps/totalcross-depot-tools/zlib-ng/local + TotalCrossVM/deps/totalcross-depot-tools/libpng/local + TotalCrossVM/deps/totalcross-depot-tools/sqlite3/local + TotalCrossVM/deps/totalcross-depot-tools/mbedtls/local + key: native-deps-${{ matrix.project }}-${{ env.SQLITE3_GITHUB_REPO }}-${{ env.SQLITE3_RELEASE_TAG }}-${{ env.ZLIB_NG_GITHUB_REPO }}-${{ env.ZLIB_NG_RELEASE_TAG }}-${{ env.LIBPNG_GITHUB_REPO }}-${{ env.LIBPNG_RELEASE_TAG }}-${{ hashFiles('TotalCrossVM/fetch-depot-tools.sh') }} + + - name: Cache C/C++ compilation + uses: actions/cache@v5 + with: + path: .ccache + key: ccache-${{ matrix.project }}-${{ hashFiles('TotalCrossVM/CMakeLists.txt', 'TotalCrossVM/src/**/*.c', 'TotalCrossVM/src/**/*.cc', 'TotalCrossVM/src/**/*.cpp', 'TotalCrossVM/src/**/*.h', 'TotalCrossVM/src/**/*.hpp', 'TotalCrossVM/modules/**/*.cmake', 'TotalCrossVM/third_party/**/*.c', 'TotalCrossVM/third_party/**/*.cc', 'TotalCrossVM/third_party/**/*.cpp', 'TotalCrossVM/third_party/**/*.h', 'TotalCrossVM/third_party/**/*.hpp', 'TotalCrossVM/third_party/**/CMakeLists.txt', 'TotalCrossVM/third_party/**/*.cmake') }} + restore-keys: | + ccache-${{ matrix.project }}- + + - name: Allow multiarch + if: ${{ matrix.use_qemu }} + run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + + - name: Build VM & Launcher + run: | + mkdir -p build .ccache + docker run --platform ${{ matrix.docker_platform }} \ + -e CCACHE_DIR=/ccache \ + -e CCACHE_BASEDIR=/sources \ + -e CCACHE_COMPRESS=true \ + -e CCACHE_MAXSIZE=1G \ + -e SQLITE3_RELEASE_TAG \ + -e SQLITE3_GITHUB_REPO \ + -e SQLITE3_GITHUB_TOKEN \ + -e ZLIB_NG_RELEASE_TAG \ + -e ZLIB_NG_GITHUB_REPO \ + -e ZLIB_NG_GITHUB_TOKEN \ + -e LIBPNG_RELEASE_TAG \ + -e LIBPNG_GITHUB_REPO \ + -e LIBPNG_GITHUB_TOKEN \ + -v ${PWD}/build:/build \ + -v ${PWD}/.ccache:/ccache \ + -v ${PWD}:/sources \ + -t totalcross/${{ matrix.project }}:v1.0.7 bash -lc ' + set -e + cmake --version + if command -v ccache >/dev/null; then + ccache --zero-stats || true + CMAKE_CCACHE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + else + echo "::warning::ccache is not installed in this Docker image; building without compiler cache" + CMAKE_CCACHE_ARGS="" + fi + cmake /sources/TotalCrossVM -DCMAKE_BUILD_TYPE=Release ${{ matrix.project == 'linux-arm32v7' && '-DPNG_ARM_NEON_OPT=0' || '' }} ${CMAKE_CCACHE_ARGS} -G Ninja && + ninja + if command -v ccache >/dev/null; then + ccache --show-stats || true + fi + ' + + - name: Fix Docker-owned files + if: ${{ always() }} + run: | + sudo chown -R "$USER:$USER" \ + build \ + .ccache \ + TotalCrossVM/deps/totalcross-depot-tools/skia/local \ + TotalCrossVM/deps/totalcross-depot-tools/zlib-ng/local \ + TotalCrossVM/deps/totalcross-depot-tools/libpng/local \ + TotalCrossVM/deps/totalcross-depot-tools/sqlite3/local \ + TotalCrossVM/deps/totalcross-depot-tools/mbedtls/local + + - name: Upload Linux build artifacts + uses: actions/upload-artifact@v7 + with: + name: ${{ matrix.project }} + path: build + + build-linux-arm32v7-cross: + name: Build `linux-arm32v7-cross` + # Temporarily disabled; remove this condition to re-enable the cross build. + if: ${{ false }} + needs: checkout-source + runs-on: ubuntu-22.04 + + steps: + - name: Download source code + uses: actions/download-artifact@v7 + with: + name: source-code + + - name: Extract source code + run: tar -xzf source-code.tar.gz + + - name: Install cross toolchain + run: | + sudo apt-get update + sudo apt-get install -y \ + cmake \ + curl \ + gcc-arm-linux-gnueabihf \ + g++-arm-linux-gnueabihf \ + git \ + ninja-build \ + rsync + + - name: Build VM & Launcher + run: | + cmake TotalCrossVM \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_TOOLCHAIN_FILE=${PWD}/TotalCrossVM/cmake/toolchains/linux-arm32v7.cmake \ + -DPNG_ARM_NEON_OPT=0 \ + -G Ninja + ninja + + build-windows: + name: Build `windows` + needs: checkout-source + runs-on: windows-2022 + + steps: + - name: Download source code + uses: actions/download-artifact@v7 + with: + name: source-code + + - name: Extract source code + shell: bash + run: tar -xzf source-code.tar.gz + + - name: Build VM + shell: cmd + run: | + mkdir TotalCrossVM\vc2022 + pushd TotalCrossVM\vc2022 + cmake ..\ -G"Visual Studio 17 2022" -A Win32 + cmake --build . --config Release + popd + + - name: Upload Windows build artifacts + uses: actions/upload-artifact@v7 + with: + name: windows + path: | + TotalCrossVM/vc2022/Release/*.dll + TotalCrossVM/vc2022/Release/*.exe + + build-sdk: + name: Build `SDK` + needs: checkout-source + runs-on: ubuntu-22.04 + + steps: + - name: Download source code + uses: actions/download-artifact@v7 + with: + name: source-code + + - name: Extract source code + run: tar -xzf source-code.tar.gz + + - name: Setup JDK 11 + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: 11 + + - name: Cache Gradle packages + uses: actions/cache@v5 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + + - name: Build SDK + working-directory: scripts + run: ./package-sdk.sh + + - name: Pack SDK build artifacts + run: tar -czf sdk-build.tar.gz build/TotalCross + + - name: Upload SDK build artifacts + uses: actions/upload-artifact@v7 + with: + name: sdk-build + path: sdk-build.tar.gz + + build-android: + name: Build `Android` + needs: checkout-source + runs-on: ubuntu-24.04 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + + steps: + - name: Download source code + uses: actions/download-artifact@v7 + with: + name: source-code + + - name: Extract source code + run: tar -xzf source-code.tar.gz + + - name: Setup JDK 17 + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: 17 + + - name: Cache artifacts + uses: actions/cache@v5 + with: + path: | + TotalCrossVM/deps/totalcross-depot-tools/skia/local + TotalCrossVM/deps/totalcross-depot-tools/zlib-ng/local + TotalCrossVM/deps/totalcross-depot-tools/libpng/local + TotalCrossVM/deps/totalcross-depot-tools/sqlite3/local + TotalCrossVM/deps/totalcross-depot-tools/mbedtls/local + TotalCrossVM/android/tcvm/src/main/jniLibs + key: native-deps-android-${{ env.SQLITE3_GITHUB_REPO }}-${{ env.SQLITE3_RELEASE_TAG }}-${{ env.ZLIB_NG_GITHUB_REPO }}-${{ env.ZLIB_NG_RELEASE_TAG }}-${{ env.LIBPNG_GITHUB_REPO }}-${{ env.LIBPNG_RELEASE_TAG }}-${{ hashFiles('TotalCrossVM/fetch-depot-tools.sh') }} + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + with: + packages: '' + + - name: Cache Gradle packages + uses: actions/cache@v5 + with: + path: | + ~/.gradle/caches/modules-2 + ~/.gradle/caches/jars-* + ~/.gradle/caches/transforms-* + ~/.gradle/wrapper + key: gradle-android-${{ runner.os }}-${{ hashFiles('TotalCrossVM/android/**/*.gradle', 'TotalCrossVM/android/gradle/wrapper/gradle-wrapper.properties') }} + restore-keys: | + gradle-android-${{ runner.os }}- + + - name: Build Android + working-directory: scripts + run: ./package-android.sh + + - name: Upload Android build artifacts + uses: actions/upload-artifact@v7 + with: + name: android + path: build/TotalCross/dist/vm/android/TotalCross.aab + + build-ios: + name: Build `iOS` + needs: checkout-source + runs-on: macos-15 + + steps: + - name: Download source code + uses: actions/download-artifact@v7 + with: + name: source-code + + - name: Extract source code + run: tar -xzf source-code.tar.gz + + - name: Cache artifacts + uses: actions/cache@v5 + with: + path: | + TotalCrossVM/deps/totalcross-depot-tools/skia/local + TotalCrossVM/deps/totalcross-depot-tools/zlib-ng/local + TotalCrossVM/deps/totalcross-depot-tools/libpng/local + TotalCrossVM/deps/totalcross-depot-tools/sqlite3/local + TotalCrossVM/deps/totalcross-depot-tools/mbedtls/local + key: native-deps-ios-${{ env.SQLITE3_GITHUB_REPO }}-${{ env.SQLITE3_RELEASE_TAG }}-${{ env.ZLIB_NG_GITHUB_REPO }}-${{ env.ZLIB_NG_RELEASE_TAG }}-${{ env.LIBPNG_GITHUB_REPO }}-${{ env.LIBPNG_RELEASE_TAG }}-${{ hashFiles('TotalCrossVM/fetch-depot-tools.sh') }} + + - name: Cache CocoaPods + uses: actions/cache@v5 + with: + path: | + TotalCrossVM/xcode/Pods + ~/Library/Caches/CocoaPods + ~/.cocoapods/repos + key: cocoapods-ios-${{ hashFiles('TotalCrossVM/xcode/Podfile') }} + restore-keys: | + cocoapods-ios- + + - name: Show tool versions + run: | + xcodebuild -version + cmake --version + pod --version + + - name: Install xcodebuild formatter + run: brew install xcbeautify + + - name: Install iOS signing assets + if: ${{ inputs.signed-ios }} + env: + IOS_CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }} + IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }} + IOS_PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }} + IOS_KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }} + run: | + set -euo pipefail + + if [ -z "$IOS_CERTIFICATE_BASE64" ] || [ -z "$IOS_CERTIFICATE_PASSWORD" ] || [ -z "$IOS_PROVISION_PROFILE_BASE64" ] || [ -z "$IOS_KEYCHAIN_PASSWORD" ]; then + echo "::error::Missing iOS signing secrets. Configure IOS_CERTIFICATE_BASE64, IOS_CERTIFICATE_PASSWORD, IOS_PROVISION_PROFILE_BASE64, and IOS_KEYCHAIN_PASSWORD." + exit 1 + fi + + certificate_path="${RUNNER_TEMP}/ios-signing-certificate.p12" + profile_path="${RUNNER_TEMP}/TotalCross_VM.mobileprovision" + profile_plist_path="${RUNNER_TEMP}/TotalCross_VM.plist" + keychain_path="${RUNNER_TEMP}/ios-signing.keychain-db" + + printf '%s' "$IOS_CERTIFICATE_BASE64" | base64 -D > "$certificate_path" + printf '%s' "$IOS_PROVISION_PROFILE_BASE64" | base64 -D > "$profile_path" + + security create-keychain -p "$IOS_KEYCHAIN_PASSWORD" "$keychain_path" + security set-keychain-settings -lut 21600 "$keychain_path" + security unlock-keychain -p "$IOS_KEYCHAIN_PASSWORD" "$keychain_path" + security import "$certificate_path" -P "$IOS_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$keychain_path" + security list-keychain -d user -s "$keychain_path" + security default-keychain -s "$keychain_path" + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$IOS_KEYCHAIN_PASSWORD" "$keychain_path" + + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + security cms -D -i "$profile_path" > "$profile_plist_path" + profile_uuid="$(/usr/libexec/PlistBuddy -c 'Print :UUID' "$profile_plist_path")" + profile_name="$(/usr/libexec/PlistBuddy -c 'Print :Name' "$profile_plist_path")" + profile_team_id="$(/usr/libexec/PlistBuddy -c 'Print :TeamIdentifier:0' "$profile_plist_path")" + profile_app_id="$(/usr/libexec/PlistBuddy -c 'Print :Entitlements:application-identifier' "$profile_plist_path")" + + if [[ "$profile_app_id" != *.com.totalcross.vm && "$profile_app_id" != *.com.totalcross.* ]]; then + echo "::error::The installed iOS provisioning profile is for '${profile_app_id}', but this build requires com.totalcross.vm or a matching wildcard profile." + exit 1 + fi + + cp "$profile_path" "$HOME/Library/MobileDevice/Provisioning Profiles/${profile_uuid}.mobileprovision" + { + echo "IOS_PROVISION_PROFILE_SPECIFIER=${profile_name}" + echo "IOS_PROVISION_PROFILE_UUID=${profile_uuid}" + echo "IOS_TEAM_ID=${profile_team_id}" + } >> "$GITHUB_ENV" + + echo "Installed iOS provisioning profile '${profile_name}' (${profile_uuid}) for team '${profile_team_id}' and app id '${profile_app_id}'." + + - name: Generate and patch Xcode project + working-directory: TotalCrossVM/xcode + run: | + cmake ../ -GXcode + ruby -pi -e "gsub(\"config.build_settings['ARCHS[sdk=iphonesimulator*]'] = 'x86_64'\", \"config.build_settings['ARCHS[sdk=iphonesimulator*]'] = 'arm64'\")" Podfile + pod install + /usr/libexec/PlistBuddy -c "Add :DisableBuildSystemDeprecationDiagnostic bool" TotalCross.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings || true + /usr/libexec/PlistBuddy -c "Set :DisableBuildSystemDeprecationDiagnostic true" TotalCross.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings + ruby ../../scripts/fix-ios-xcode-dependencies.rb TCVM.xcodeproj/project.pbxproj + + - name: Build iOS archive for device + working-directory: TotalCrossVM/xcode + env: + SIGN_IOS: ${{ inputs.signed-ios }} + run: | + set -o pipefail + mkdir -p build/logs + + signing_args=() + if [ "$SIGN_IOS" = "true" ]; then + signing_args=( + DEVELOPMENT_TEAM="${IOS_TEAM_ID}" + PROVISIONING_PROFILE_SPECIFIER="${IOS_PROVISION_PROFILE_SPECIFIER}" + teamID="${IOS_TEAM_ID}" + ) + else + signing_args=( + CODE_SIGNING_ALLOWED=NO + ) + fi + + xcodebuild \ + -workspace TotalCross.xcworkspace \ + -scheme TotalCross \ + BUILD_DIR=${PWD} \ + -configuration Release \ + -sdk iphoneos \ + -destination 'generic/platform=iOS' \ + -archivePath build/TotalCross.xcarchive \ + -resultBundlePath build/logs/TotalCross.xcresult \ + "${signing_args[@]}" \ + archive \ + 2>&1 | tee build/logs/xcodebuild-raw.log | xcbeautify + + - name: Export signed iOS IPA + if: ${{ inputs.signed-ios }} + working-directory: TotalCrossVM/xcode + run: | + set -o pipefail + /usr/libexec/PlistBuddy -c "Set :teamID ${IOS_TEAM_ID}" ExportOptions.plist + /usr/libexec/PlistBuddy -c "Set :provisioningProfiles:com.totalcross.vm ${IOS_PROVISION_PROFILE_SPECIFIER}" ExportOptions.plist + xcodebuild \ + -exportArchive \ + -archivePath build/TotalCross.xcarchive \ + -exportPath build/TotalCross.ipa \ + -exportOptionsPlist ExportOptions.plist \ + 2>&1 | tee build/logs/xcodebuild-export-raw.log | xcbeautify + + - name: Upload iOS xcodebuild diagnostics + if: always() + uses: actions/upload-artifact@v7 + with: + name: ios-xcodebuild-diagnostics + path: | + TotalCrossVM/xcode/build/logs/xcodebuild-raw.log + TotalCrossVM/xcode/build/logs/xcodebuild-export-raw.log + TotalCrossVM/xcode/build/logs/TotalCross.xcresult + if-no-files-found: warn + + - name: Pack iOS artifacts + env: + SIGN_IOS: ${{ inputs.signed-ios }} + run: | + if [ "$SIGN_IOS" = "true" ]; then + tar -czf ios.tar.gz -C TotalCrossVM/xcode/build TotalCross.xcarchive TotalCross.ipa + else + tar -czf ios.tar.gz -C TotalCrossVM/xcode/build TotalCross.xcarchive + fi + + - name: Upload iOS build artifacts + uses: actions/upload-artifact@v7 + with: + name: ios + path: ios.tar.gz diff --git a/.github/workflows/commit.yml b/.github/workflows/commit.yml index f78de571e7..1f3b614760 100644 --- a/.github/workflows/commit.yml +++ b/.github/workflows/commit.yml @@ -1,49 +1,126 @@ -name: 'Commit Message Check' -on: [push] +name: Commit Message Check + +on: + push: + branches: + - master + pull_request: jobs: check-commit-message: - name: Check Commit Message - runs-on: ubuntu-20.04 - steps: + name: Validate commit messages + runs-on: ubuntu-latest - - name: Check Commit Type - uses: gsactions/commit-message-checker@v1 + steps: + - name: Check out repository + uses: actions/checkout@v6 with: - pattern: '^[a-z0-9,\s]+: .+(?:\n(?:\n.+)+)?(?:\n\n.+)?$' - error: 'Your message must have the correct format ": " with an optional body and footer separated by blank lines.' + fetch-depth: 0 - - name: Check Title Capitalize - uses: gsactions/commit-message-checker@v1 - with: - pattern: '^[^A-Z]' - flags: '' - error: 'Your title should not capitalize first word.' + - name: Validate commit messages in range + env: + EVENT_NAME: ${{ github.event_name }} + PUSH_BEFORE: ${{ github.event.before }} + PUSH_AFTER: ${{ github.sha }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + python3 <<'PY' + import os + import re + import subprocess + import sys - - name: Check Title Length - uses: gsactions/commit-message-checker@v1 - with: - pattern: '^[^\s]+([ \t]+[^\s]+){2,}[ \t]*(\n.*)?$' - flags: 's' - error: 'A meaningful title should contain at least 3 words.' + TITLE_FORMAT = re.compile( + r'^(fix|feat|refactor|perf|style|test|docs|build|ci|chore|revert)' + r'(!\([a-z0-9_-]+(,[a-z0-9_-]+)?\)|' + r'\([a-z0-9_-]+(,[a-z0-9_-]+)?\)!?): [a-z0-9 ].*$' + ) + TITLE_CAPITALIZATION = re.compile(r'^[^A-Z]') + TITLE_WORD_COUNT = re.compile(r'^[^ ]+([ \t]+[^ ]+){2,}$', re.S) + TITLE_LENGTH = re.compile(r'^([^\n]{1,80})(\n.*)?$', re.S) + BLANK_LINE = re.compile(r'^[^\n]+(\n\n.+)?$', re.S) + BODY_LINE_LENGTH = re.compile(r'^[^\n]+(\n[^\n]{0,80})*$', re.S) - - name: Check Title Line Length - uses: gsactions/commit-message-checker@v1 - with: - pattern: '^([^\n]{1,80}|Merge pull request.*)(\n.*)?$' - flags: 's' - error: 'The maximum title line length of 80 characters is exceeded.' + def run(*args): + return subprocess.check_output(args, text=True).strip() - - name: Check Title Line Separator - uses: gsactions/commit-message-checker@v1 - with: - pattern: '^[^\n]+(\n\n.+)?$' - flags: 's' - error: 'Should leave an empty line after title.' + def commit_range(): + event_name = os.environ["EVENT_NAME"] + if event_name == "pull_request": + head = os.environ["PR_HEAD_SHA"] + base = run("git", "merge-base", os.environ["PR_BASE_SHA"], head) + return base, head - - name: Check Line Length - uses: gsactions/commit-message-checker@v1 - with: - pattern: '^[^\n]+(\n[^\n]{0,80})*$' - flags: 's' - error: 'The maximum line length of 80 characters is exceeded.' + before = os.environ["PUSH_BEFORE"] + after = os.environ["PUSH_AFTER"] + if before == "0000000000000000000000000000000000000000": + return "", after + return before, after + + def list_commits(base, head): + if base: + output = run("git", "rev-list", f"{base}..{head}") + else: + output = run("git", "rev-list", head) + return [line for line in output.splitlines() if line] + + def read_message(commit): + return subprocess.check_output( + ["git", "show", "-s", "--format=%B", commit], + text=True, + ).rstrip("\n") + + def validate(commit, message): + title = message.split("\n", 1)[0] + failures = [] + + if not TITLE_FORMAT.match(title): + failures.append("Invalid commit title format.") + if not TITLE_CAPITALIZATION.match(title): + failures.append("Commit title must not start with an uppercase letter.") + if not TITLE_WORD_COUNT.match(title): + failures.append("Commit title must contain at least 3 words.") + if not TITLE_LENGTH.match(message): + failures.append("Commit title must be 80 characters or less.") + if title.endswith("."): + failures.append("Commit title must not end with a period.") + if not BLANK_LINE.match(message): + failures.append( + "If a commit body is present, it must be separated from the title by a blank line." + ) + if not BODY_LINE_LENGTH.match(message): + failures.append("Each line in the commit body must be 80 characters or less.") + + return title, failures + + base, head = commit_range() + commits = list_commits(base, head) + range_label = f"{base}..{head}" if base else head + print(f"Validating commit messages in range: {range_label}") + print(f"Event: {os.environ['EVENT_NAME']}") + + if not commits: + print("No commits to validate.") + sys.exit(0) + + invalid = [] + for commit in reversed(commits): + message = read_message(commit) + title, failures = validate(commit, message) + if failures: + invalid.append((commit, title, failures)) + + if invalid: + print(f"Commit message validation failed for {len(invalid)} commit(s).\n") + print("Commits outside the expected format:") + for commit, title, failures in invalid: + print(f"- {commit} {title}") + for failure in failures: + print(f" - {failure}") + print(f"::error title={commit[:10]} {title}::{failure}") + print() + sys.exit(1) + + print(f"Validated {len(commits)} commit message(s).") + PY diff --git a/.github/workflows/docker-linux-images.yml b/.github/workflows/docker-linux-images.yml new file mode 100644 index 0000000000..e3d45f8708 --- /dev/null +++ b/.github/workflows/docker-linux-images.yml @@ -0,0 +1,68 @@ +name: Docker Linux images + +on: + workflow_dispatch: + inputs: + tag: + description: Docker image tag to publish + required: true + default: v1.0.4 + push_latest: + description: Also publish the latest tag + required: true + default: false + type: boolean + +jobs: + build-linux-images: + name: Build ${{ matrix.image }}:${{ inputs.tag }} + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + include: + - image: totalcross/linux-amd64 + dockerfile: TotalCrossVM/docker/amd64/Dockerfile + platform: linux/amd64 + - image: totalcross/linux-arm32v7 + dockerfile: TotalCrossVM/docker/arm32v7/Dockerfile + platform: linux/arm/v7 + - image: totalcross/linux-arm64 + dockerfile: TotalCrossVM/docker/arm64/Dockerfile + platform: linux/arm64 + + steps: + - name: Checkout source code + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build image tags + id: image-tags + shell: bash + run: | + tags="${{ matrix.image }}:${{ inputs.tag }}" + if [ "${{ inputs.push_latest }}" = "true" ]; then + tags="${tags},${{ matrix.image }}:latest" + fi + echo "tags=${tags}" >> "$GITHUB_OUTPUT" + + - name: Build and push image + uses: docker/build-push-action@v7 + with: + context: TotalCrossVM + file: ${{ matrix.dockerfile }} + platforms: ${{ matrix.platform }} + push: true + tags: ${{ steps.image-tags.outputs.tags }} diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index f2059eb745..0666255fe5 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -7,58 +7,6 @@ on: branches: [ master ] jobs: - build-linux: - name: Build '${{ matrix.project }}' - strategy: - fail-fast: false - matrix: - project: [ linux-amd64, linux-arm32v7, linux-arm64 ] - - runs-on: ubuntu-20.04 - - steps: - - name: Checkout source code - uses: actions/checkout@v2 - - - name: Allow multiarch - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - - - name: Build VM & Launcher - run: | - docker run -v ${PWD}:/sources -t totalcross/${{ matrix.project }}:v1.0.0 bash -c "cmake /sources/TotalCrossVM -DCMAKE_BUILD_TYPE=Release -G Ninja && ninja" - - build-sdk-and-android: - name: Build `${{ matrix.project.name }}` - runs-on: ubuntu-18.04 - container: - image: docker://fabernovel/android:api-29-ndk-v1.2.0 - strategy: - fail-fast: false - matrix: - project: - - name: SDK - command: ./gradlew dist -x test --stacktrace - directory: TotalCrossSDK - - name: Android - command: ./gradlew assembleRelease -x test --stacktrace - directory: TotalCrossVM/android - - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Setup JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle - - - name: Build ${{ matrix.project.name }} - working-directory: ${{ matrix.project.directory }} - run: ${{ matrix.project.command }} + build: + uses: ./.github/workflows/build.yml + secrets: inherit diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 9b98d725ef..dde2c2405b 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -6,119 +6,31 @@ on: - cron: "0 1 * * 1" jobs: - build_linux: - name: Build '${{ matrix.project }}' - strategy: - fail-fast: false - matrix: - project: [ linux-amd64, linux-arm32v7, linux-arm64 ] - - runs-on: ubuntu-20.04 - - steps: - - name: Checkout source code - uses: actions/checkout@v2 - - - name: Allow multiarch - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - - - name: Build VM & Launcher - run: | - mkdir -p build - docker run -v ${PWD}/build:/build -v ${PWD}:/sources -t totalcross/${{ matrix.project }}:v1.0.0 bash -c "cmake /sources/TotalCrossVM -DCMAKE_BUILD_TYPE=Release -G Ninja && ninja" - - - name: Upload artifacts - uses: actions/upload-artifact@v2 - with: - name: ${{ matrix.project }} - path: build - - build_sdk_android: - name: Build SDK and Android + build: + uses: ./.github/workflows/build.yml + with: + signed-ios: true + secrets: inherit + + package_totalcross: + name: Package TotalCross if: ${{ always() }} - needs: build_linux - runs-on: ubuntu-18.04 - container: - image: docker://fabernovel/android:api-29-ndk-v1.2.0 + needs: build + runs-on: ubuntu-22.04 steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: set up JDK 11 - uses: actions/setup-java@v1 + - name: Download SDK build artifacts + uses: actions/download-artifact@v4 with: - java-version: 11 + name: sdk-build + path: . - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle - - - name: Download Dependences S3 Libs - uses: prewk/s3-cp-action@master - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE: 's3://${{ secrets.S3_BUCKET }}/libs.zip' - DEST: 'libs.zip' - - - name: Download Dependences S3 Tools - uses: prewk/s3-cp-action@master - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE: 's3://${{ secrets.S3_BUCKET }}/tools.zip' - DEST: 'tools.zip' - - - name: Download Dependences S3 LitebaseLib.tcz - uses: prewk/s3-cp-action@master - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE: 's3://${{ secrets.S3_BUCKET }}/LitebaseLib.tcz' - DEST: 'LitebaseLib.tcz' - - - name: Download Dependences S3 Android Key - uses: prewk/s3-cp-action@master - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE: 's3://${{ secrets.S3_BUCKET }}/key_android.zip' - DEST: 'key_android.zip' - - - name: Download Dependences S3 Docs Litebase - uses: prewk/s3-cp-action@master - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE: 's3://${{ secrets.S3_BUCKET }}/docs_litebase.zip' - DEST: 'docs_litebase.zip' - - - name: Download Dependences S3 Sources Litebase - uses: prewk/s3-cp-action@master - env: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SOURCE: 's3://${{ secrets.S3_BUCKET }}/lb.zip' - DEST: 'lb.zip' - - - name: Build SDK - working-directory: TotalCrossSDK - run: | - ./gradlew dist -x test --stacktrace - cp build/libs/totalcross-sdk-* dist - - - name: Build Android - working-directory: TotalCrossVM/android - run: | - ./gradlew assembleRelease -x test --stacktrace + - name: Extract SDK build artifacts + run: tar -xzf sdk-build.tar.gz - name: Download Linux amd64 continue-on-error: true - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: linux-amd64 path: linux-amd64 @@ -126,14 +38,14 @@ jobs: - name: Manage Linux amd64 files if: ${{ success() }} run: | - mkdir -p TotalCrossSDK/dist/vm/linux - mkdir -p TotalCrossSDK/etc/launchers/linux - cp -p -a -R linux-amd64/libtcvm.so TotalCrossSDK/dist/vm/linux/ - cp -p -a -R linux-amd64/Launcher TotalCrossSDK/etc/launchers/linux/ + mkdir -p build/TotalCross/dist/vm/linux + mkdir -p build/TotalCross/etc/launchers/linux + cp -p -a -R linux-amd64/libtcvm.so build/TotalCross/dist/vm/linux/ + cp -p -a -R linux-amd64/Launcher build/TotalCross/etc/launchers/linux/ - name: Download Linux arm32 continue-on-error: true - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: linux-arm32v7 path: linux-arm32v7 @@ -141,14 +53,14 @@ jobs: - name: Manage Linux arm32 files if: ${{ success() }} run: | - mkdir -p TotalCrossSDK/dist/vm/linux_arm - mkdir -p TotalCrossSDK/etc/launchers/linux_arm - cp -p -a -R linux-arm32v7/libtcvm.so TotalCrossSDK/dist/vm/linux_arm/ - cp -p -a -R linux-arm32v7/Launcher TotalCrossSDK/etc/launchers/linux_arm/ + mkdir -p build/TotalCross/dist/vm/linux_arm + mkdir -p build/TotalCross/etc/launchers/linux_arm + cp -p -a -R linux-arm32v7/libtcvm.so build/TotalCross/dist/vm/linux_arm/ + cp -p -a -R linux-arm32v7/Launcher build/TotalCross/etc/launchers/linux_arm/ - name: Download Linux arm64 continue-on-error: true - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: name: linux-arm64 path: linux-arm64 @@ -156,30 +68,96 @@ jobs: - name: Manage Linux arm64 files if: ${{ success() }} run: | - mkdir -p TotalCrossSDK/dist/vm/linux_arm64 - mkdir -p TotalCrossSDK/etc/launchers/linux_arm64 - cp -p -a -R linux-arm64/libtcvm.so TotalCrossSDK/dist/vm/linux_arm64/ - cp -p -a -R linux-arm64/Launcher TotalCrossSDK/etc/launchers/linux_arm64/ + mkdir -p build/TotalCross/dist/vm/linux_arm64 + mkdir -p build/TotalCross/etc/launchers/linux_arm64 + cp -p -a -R linux-arm64/libtcvm.so build/TotalCross/dist/vm/linux_arm64/ + cp -p -a -R linux-arm64/Launcher build/TotalCross/etc/launchers/linux_arm64/ + + - name: Download Windows + continue-on-error: true + uses: actions/download-artifact@v4 + with: + name: windows + path: windows + + - name: Manage Windows files + if: ${{ success() }} + shell: bash + run: | + mkdir -p build/TotalCross/dist/vm/win32 + mkdir -p build/TotalCross/etc/launchers/win32 + find windows -iname "tcvm.dll" -exec cp -p -a -R {} build/TotalCross/dist/vm/win32/TCVM.dll \; -quit + find windows -iname "launcher.exe" -exec cp -p -a -R {} build/TotalCross/etc/launchers/win32/Launcher.exe \; -quit + + - name: Download Android + continue-on-error: true + uses: actions/download-artifact@v4 + with: + name: android + path: android + + - name: Manage Android files + if: ${{ success() }} + shell: bash + run: | + android_artifact="$(find android -name "TotalCross.aab" -type f -print -quit)" + if [ -z "$android_artifact" ]; then + echo "Could not find TotalCross.aab in Android build artifact" >&2 + exit 1 + fi + + mkdir -p build/TotalCross/dist/vm/android + mkdir -p build/TotalCross/etc/launchers/android + mkdir -p build/TotalCross/etc/tools/android + cp -p -a -R "$android_artifact" build/TotalCross/dist/vm/android/TotalCross.aab + + - name: Download iOS + continue-on-error: true + uses: actions/download-artifact@v4 + with: + name: ios + path: ios - - name: Manager files + - name: Manage iOS files + if: ${{ success() }} + shell: bash run: | - rm -rf TotalCrossSDK/docs/* && mv TotalCrossSDK/build/docs/javadoc TotalCrossSDK/docs/html - mv LitebaseLib.tcz TotalCrossSDK/dist/vm/ - find TotalCrossSDK -name .DS_Store -exec rm -rf -- {} + - find TotalCrossSDK -name "*git*" -exec rm -rf -- {} + - find TotalCrossSDK -name "*gradle*" -type f -exec rm -rf -- {} + && find TotalCrossSDK -name "*gradle*" -type d -exec rm -rf -- {} + - rm -rf TotalCrossSDK/build TotalCrossSDK/build.xml TotalCrossSDK/proguard.txt TotalCrossSDK/src/test - unzip -n libs.zip -x "__MACOSX/*" -d TotalCrossSDK/etc/ - unzip -n tools.zip -x "__MACOSX/*" -d TotalCrossSDK/etc/ - unzip -n key_android.zip -d TotalCrossSDK/etc/ - unzip -n lb.zip -d TotalCrossSDK/src/ - unzip -n docs_litebase.zip -d TotalCrossSDK/docs/html + mkdir -p build/TotalCross/dist/vm/ios + mkdir -p build/TotalCross/etc/tools/iOSCodesign + mkdir -p ios/extracted + tar -xzf ios/ios.tar.gz -C ios/extracted + cp -p -a -R ios/extracted/TotalCross.xcarchive build/TotalCross/etc/tools/iOSCodesign/ + cp -p -a ios/extracted/TotalCross.ipa/TotalCross.ipa build/TotalCross/dist/vm/ios/ + + - name: Read TotalCross version + id: totalcross-version + shell: bash + run: | + version="$(find build/TotalCross/dist -maxdepth 1 -type f -regex '.*/totalcross-sdk-[0-9]+\.[0-9]+\.[0-9]+\.jar' -print -quit)" + version="${version##*/}" + version="${version#totalcross-sdk-}" + version="${version%.jar}" + if [ -z "$version" ]; then + echo "Could not determine TotalCross version from packaged SDK jar" >&2 + exit 1 + fi + + echo "version=${version}" >> "$GITHUB_OUTPUT" + + - name: Create packaged SDK zip + shell: bash + run: | + cd build + zip -r "TotalCross-${{ steps.totalcross-version.outputs.version }}.zip" TotalCross - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: - name: TotalCross - path: | - TotalCrossSDK/ - + name: TotalCross-${{ steps.totalcross-version.outputs.version }} + path: build/TotalCross-${{ steps.totalcross-version.outputs.version }}.zip + - name: Upload dist jars + uses: actions/upload-artifact@v4 + with: + name: TotalCross-${{ steps.totalcross-version.outputs.version }}-jars + path: build/TotalCross/dist/*.jar diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae23412b82..94bc38cb47 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,51 +10,108 @@ on: description: 'Version (name of the tag to be referenced)' required: true +permissions: + contents: write + id-token: write + jobs: - build: - name: Upload Release Asset + release: + name: Create Release runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 - - - name: Download last artifact + uses: actions/checkout@v6 + + - name: Resolve release version + id: release-version + shell: bash + run: | + tag="${{ github.event.inputs.tag }}" + if [ -z "$tag" ]; then + tag="${GITHUB_REF_NAME}" + fi + + version="${tag#v}" + if [ -z "$version" ]; then + echo "Could not determine release version" >&2 + exit 1 + fi + + echo "tag=${tag}" >> "$GITHUB_OUTPUT" + echo "version=${version}" >> "$GITHUB_OUTPUT" + + - name: Download packaged SDK + uses: dawidd6/action-download-artifact@v2 + with: + workflow: package.yml + workflow_conclusion: success + name: TotalCross-${{ steps.release-version.outputs.version }} + path: release-assets + + - name: Download packaged jars uses: dawidd6/action-download-artifact@v2 with: workflow: package.yml workflow_conclusion: success - name: TotalCross - path: ../TotalCross + name: TotalCross-${{ steps.release-version.outputs.version }}-jars + path: release-assets/jars - - name: "ZIP TotalCross" + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::801206326054:role/github-action-release + aws-region: us-east-1 + + - name: Upload release zip to S3 + shell: bash run: | - zip -r TotalCross-${{ github.event.inputs.tag }}.zip ../TotalCross + version="${{ steps.release-version.outputs.version }}" + zip_asset="release-assets/TotalCross-${version}.zip" + version_prefix="$(echo "$version" | awk -F. '{ print $1 "." $2 }')" + s3_uri="s3://totalcross-release/${version_prefix}/TotalCross-${version}.zip" + + if [ ! -f "$zip_asset" ]; then + echo "Could not find release zip: $zip_asset" >&2 + exit 1 + fi - - name: "CHANGELOG Release ${{ github.event.inputs.tag }}" + aws s3 cp "$zip_asset" "$s3_uri" + + - name: Extract release notes run: | - version=$(echo '${{ github.event.inputs.tag }}' | sed 's/v//g') - sed -n '/^## '$version'/,/^## /p' CHANGELOG.md | sed -e '$d' > CHANGELOG-${{ github.event.inputs.tag }}.md - + version="${{ steps.release-version.outputs.version }}" + awk -v version="$version" ' + $0 ~ "^## \\[" version "\\] - [0-9]{4}-[0-9]{2}-[0-9]{2}$" { + emit = 1 + print + next + } + emit && /^## \[[0-9]+\.[0-9]+\.[0-9]+\] - [0-9]{4}-[0-9]{2}-[0-9]{2}$/ { + exit + } + emit { + print + } + ' CHANGELOG.md > "CHANGELOG-${{ steps.release-version.outputs.tag }}.md" + + if [ ! -s "CHANGELOG-${{ steps.release-version.outputs.tag }}.md" ]; then + echo "Could not find changelog entry for ${version}" >&2 + exit 1 + fi + - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.event.inputs.tag }} - release_name: Release ${{ github.event.inputs.tag }} - body_path: CHANGELOG-${{ github.event.inputs.tag }}.md - draft: false - prerelease: false - - - name: Upload Release ZIP - id: upload-zip - uses: actions/upload-release-asset@v1 + shell: bash env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./TotalCross-${{ github.event.inputs.tag }}.zip - asset_name: TotalCross-${{ github.event.inputs.tag }}.zip - asset_content_type: application/zip + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + tag="${{ steps.release-version.outputs.tag }}" + version="${{ steps.release-version.outputs.version }}" + notes="CHANGELOG-${tag}.md" + zip_asset="release-assets/TotalCross-${version}.zip" + gh release create "$tag" \ + --verify-tag \ + --title "Release ${tag}" \ + --notes-file "$notes" \ + "$zip_asset" \ + release-assets/jars/*.jar diff --git a/.gitignore b/.gitignore index d49436e4df..0d654c0d37 100644 --- a/.gitignore +++ b/.gitignore @@ -47,15 +47,18 @@ Breakpoints.xcbkptlist # Files from VS Code /**/.vs +.history #CMake -build/ +build*/ #Gradle .gradle # Git *.orig +TotalCrossVM/deps/totalcross-depot-tools/ +TotalCrossVM/android/tcvm/src/main/jniLibs/**/libskia.so # JetBrain's IDEs TotalCrossVM/android/.idea @@ -66,3 +69,9 @@ TotalCrossSDK/.idea TotalCrossVM/android/src/main/libs/armeabi/ TotalCrossVM/android/app/src/main/assets/tcfiles\.zip TotalCrossVM/android/app/.cxx + +# CMake generated files +CMakeFiles +_deps +CMakeCache.txt +.cxx diff --git a/.gitmessage.txt b/.gitmessage.txt new file mode 100644 index 0000000000..469055cba2 --- /dev/null +++ b/.gitmessage.txt @@ -0,0 +1,61 @@ +# ---------------------------------------------------------------------- +# Commit message template +# +# Format: +# ([,]): short description +# !([,]): short description +# ([,])!: short description +# +# Rules for the title: +# - Use one of: fix, feat, refactor, perf, style, test, docs, build, ci, chore, +# revert +# - Scope is required, such as vm, runtime, gc, sdk, compiler, socket, cmake +# - Platform is optional after the scope, such as android, ios, linux, windows +# - Do not include architecture in the title; mention it in the body if relevant +# - Use the imperative mood (fix, add, remove, improve) +# - Start with a lowercase letter +# - At least 3 words +# - Maximum 80 characters +# - Do not end with a period +# - Use ! for breaking changes +# +# Examples: +# fix(vm): handle null class loader +# feat(sdk,android): add camera permission helper +# refactor(gc): simplify mark queue handling +# build(cmake,windows): link bundled sqlite +# feat(sdk)!: remove legacy deploy option +# revert(sdk): restore legacy deploy option +# ---------------------------------------------------------------------- + +([,]): short description + +# ---------------------------------------------------------------------- +# Commit body (optional but strongly recommended when applicable) +# +# Use the body to explain: +# - WHY the change was made +# - WHAT problem it solves +# - Any important constraints or trade-offs +# - Architecture details, if relevant (for example, arm64, x86, x64) +# - Reverted commit hashes, for revert commits +# - Rejected alternatives, if relevant +# +# Guidelines: +# - Wrap lines at 80 characters +# - Do not repeat the title +# - Prefer technical, precise language +# +# Example: +# This fixes an invalid GC mark when objects are promoted +# during minor collection. +# +# Without this change, promoted objects could be rescanned, +# leading to invalid memory access under heavy allocation. +# +# References: +# Fixes #123 +# +# Breaking-change footer: +# BREAKING CHANGE: explain compatibility impact and migration +# ---------------------------------------------------------------------- diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..4453239834 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,247 @@ +# AGENTS.md + +Guidance for automated coding agents working in this repository. + +## Project Overview + +TotalCross is a cross-platform SDK and native VM/runtime. The repository mixes +Java SDK code, C/C++ VM code, Android/iOS packaging, CMake build logic, shell +packaging scripts, and GitHub Actions workflows. + +Important top-level areas: + +- `TotalCrossSDK/`: Java SDK, Gradle build, Java sources, resources, docs, and + generated SDK jars/tcz files. +- `TotalCrossVM/`: native VM/runtime, CMake build, platform launchers, Android + Gradle project, and native dependency bootstrap. +- `TotalCrossVM/deps/totalcross-depot-tools/`: local checkout/cache for native + dependency scripts and artifacts. This directory is generated/fetched and + should not be treated as ordinary source in this repository. +- `scripts/`: packaging and platform helper scripts. Prefer updating these when + CI packaging behavior should match local packaging. +- `.github/workflows/`: CI workflows. `build.yml` is reusable through + `workflow_call`; `merge.yml` and `package.yml` consume it. + +## General Rules + +- Keep changes focused. Do not mix unrelated refactors, formatting, and build + fixes in the same change. +- Preserve user/local changes. This repository is often used with a dirty + worktree, generated outputs, local logs, and downloaded native artifacts. +- Do not commit generated dependency artifacts from `TotalCrossVM/deps`, + `TotalCrossVM/**/local`, `.cxx`, `build`, Gradle output directories, or local + logs unless the user explicitly asks for that. +- When searching, prefer `rg` and `rg --files`. +- When editing shell scripts, keep them POSIX/Bash friendly and avoid printing + secrets, tokens, private repository names, or authenticated URLs. +- For package layout changes, compare with the relevant script in `scripts/` + before changing only the workflow. + +## Build And Test Commands + +Use the smallest relevant validation for the change. + +SDK: + +```bash +cd TotalCrossSDK +./gradlew clean dist -x test +``` + +Android: + +```bash +cd TotalCrossVM/android +./gradlew :tcvm:fetchNativeDependencies +./gradlew :tcvm:externalNativeBuildCleanRelease +./gradlew :app:assembleStandardRelease +``` + +Native VM with CMake: + +```bash +cmake -S TotalCrossVM -B build -DCMAKE_BUILD_TYPE=Release -G Ninja +ninja -C build +``` + +Packaging: + +```bash +scripts/package-sdk.sh +scripts/package-android.sh +scripts/package-ios.sh +scripts/package.sh +``` + +Run full platform builds only when needed; some require Docker, Xcode, +Android SDK/NDK, Visual Studio, credentials, or private artifacts. + +## Native Dependencies + +Native dependencies are provided through `totalcross-depot-tools` and are +fetched into: + +- `TotalCrossVM/deps/totalcross-depot-tools/zlib-ng/local` +- `TotalCrossVM/deps/totalcross-depot-tools/libpng/local` +- `TotalCrossVM/deps/totalcross-depot-tools/sqlite3/local` +- `TotalCrossVM/deps/totalcross-depot-tools/mbedtls/local` +- `TotalCrossVM/deps/totalcross-depot-tools/skia/local` + +The bootstrap script is: + +```bash +TotalCrossVM/fetch-depot-tools.sh +``` + +The Android Gradle module uses `fetchNativeDependencies` to fetch the Android +prebuilt artifacts before CMake tasks. If native dependencies are missing in a +local build, run: + +```bash +cd TotalCrossVM/android +./gradlew :tcvm:fetchNativeDependencies +``` + +SQLite configuration is controlled by environment variables: + +- `SQLITE3_VARIANT`, usually `plain` or a SEE/encrypted variant +- `SQLITE3_RELEASE_TAG` +- `SQLITE3_GITHUB_REPO` +- `SQLITE3_GITHUB_TOKEN` + +mbedTLS may use: + +- `MBEDTLS_RELEASE_TAG` +- `MBEDTLS_GITHUB_REPO` +- `MBEDTLS_GITHUB_TOKEN` + +libpng/zlib-ng may use: + +- `LIBPNG_RELEASE_TAG` +- `LIBPNG_GITHUB_REPO` +- `LIBPNG_GITHUB_TOKEN` +- `ZLIB_NG_RELEASE_TAG` +- `ZLIB_NG_GITHUB_REPO` +- `ZLIB_NG_GITHUB_TOKEN` + +Do not expose token values in scripts, logs, CMake messages, Gradle output, or +workflow output. Avoid echoing private repository URLs when the URL itself is +sensitive. + +## iOS Build Notes + +- The iOS bundle under `TotalCrossVM/xcode` contains two projects with + different roles: + - `TotalCross.xcodeproj` is checked-in app scaffolding and should stay + stable. + - `TCVM.xcodeproj` is generated by `cmake ../ -GXcode` and is the correct + place to solve native VM dependency linkage. +- When adding or changing native static dependencies for iOS, prefer solving + them in `TotalCrossVM/CMakeLists.txt` using Xcode/CMake settings on the + generated `tcvm` target, such as `CMAKE_STATIC_LINKER_FLAGS`, + `CMAKE_XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS`, + `CMAKE_XCODE_ATTRIBUTE_LINK_BINARIES_WITH_LIBRARIES`, or + `XCODE_ATTRIBUTE_OTHER_LIBTOOLFLAGS`. +- For depot-tools static prebuilts on iOS, prefer passing the resolved archive + paths directly through `XCODE_ATTRIBUTE_OTHER_LIBTOOLFLAGS` so `libtool` + folds them into `libtcvm.a`. Avoid relying on short linker names such as + `-lsqlite3`, `-lpng16`, or `-lz`, because Xcode may resolve them in the wrong + order or against SDK/system libraries instead of the intended prebuilt. +- When possible, model iOS prebuilts as imported CMake targets and link them to + `tcvm` with `XCODE_LINK_BUILD_PHASE_MODE` set to `KNOWN_LOCATION`, so the + generated `TCVM.xcodeproj` carries concrete file references instead of + depending only on linker flag strings. +- If Xcode still leaves unresolved symbols from a static dependency, prefer a + CMake-managed `POST_BUILD` merge step with `/usr/bin/libtool -static` to fold + the prebuilt archives into `libtcvm.a`, instead of patching the checked-in + app project. +- Do not edit `TotalCrossVM/xcode/TotalCross.xcodeproj/project.pbxproj` just to + make depot-tools native archives link. If iOS needs extra native archives, + integrate them through the generated `TCVM.xcodeproj` behavior in CMake. +- Treat Ruby/Xcode patch scripts as a last resort for depot-tools native + libraries; they should not be the primary linkage mechanism for `libpng`, + `zlib-ng`, or similar prebuilts. +- Validate iOS native dependency changes against the same flow used by + `scripts/build-ios.sh`: + +```bash +cd TotalCrossVM/xcode +cmake ../ -GXcode +pod install +ruby ../../scripts/fix-ios-xcode-dependencies.rb TCVM.xcodeproj/project.pbxproj +xcodebuild -workspace TotalCross.xcworkspace -scheme TotalCross archive +``` + +- When an iOS build still fails after CMake changes, inspect the generated + Xcode link line to confirm whether symbols are meant to be resolved during + `libtcvm.a` creation inside `tcvm.xcodeproj` or later by the final + `TotalCross` app link. + +## GitHub Actions + +The main reusable build workflow is `.github/workflows/build.yml`. + +Current CI patterns: + +- Source is packed once by `checkout-source` and then consumed by build jobs. +- Native dependency caches use `TotalCrossVM/deps/totalcross-depot-tools/*/local`. +- Linux Docker builds use versioned images such as + `totalcross/linux-amd64:v1.0.7`. +- Linux C/C++ builds use `ccache` when available. +- The `linux-arm32v7-cross` job is intentionally disabled with `if: false`. + +When changing package behavior, keep `package.yml` equivalent to the relevant +local packaging scripts, especially `scripts/package-sdk.sh`. + +## Commit Message Rules + +Follow `CONTRIBUTING.md`. Commit titles must use: + +```text +([,]): short description +!([,]): short description +([,])!: short description +``` + +Rules: + +- English only. +- Allowed types: `fix`, `feat`, `refactor`, `perf`, `style`, `test`, `docs`, + `build`, `ci`, `chore`, `revert`. +- Scope is required. Common scopes include `vm`, `runtime`, `gc`, `sdk`, + `compiler`, `socket`, `ssl`, `cmake`, `packager`, and `deploy`. +- Platform remains optional after the scope. +- Architecture is not part of the title format; mention it in the body when + relevant. +- Use `!` and a `BREAKING CHANGE:` footer when compatibility is broken and + impact or migration needs explanation. +- For `revert` commits, reference the reverted commit hash or hashes in the + body. +- Title starts with a lowercase letter. +- Title has at least 3 words and at most 80 characters. +- Use imperative mood. +- Do not end the title with a period. +- Separate body from title with a blank line when a body is present. +- Use the body to explain the motivation for non-trivial changes. + +Common examples: + +```text +build(cmake): fetch native deps from depot tools +feat(sdk,android): add camera permission helper +fix(sqlite,windows): link required static crypto libraries +feat(sdk)!: remove legacy deploy option +revert(sdk): restore legacy deploy option +``` + +## Safety Notes + +- Never run destructive git commands such as `git reset --hard` or + `git checkout --` unless explicitly requested. +- Do not remove local caches or generated directories just to make status clean. +- If a CMake/Gradle clean fails because of stale Android ABI directories under + `.cxx`, prefer fixing the build logic or removing only the stale generated + ABI directory, not source files. +- Treat `TotalCrossVM/deps/totalcross-depot-tools` as a fetched dependency + checkout. Changes needed there should usually be made in the depot-tools + repository and then consumed here through the bootstrap/ref mechanism. diff --git a/CHANGELOG.md b/CHANGELOG.md index eaf87f51ae..de2d7fd471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,511 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [7.2.0] - 2026-06-08 + +### Features + +- **sqlite:** + - update sqlite to 3.36.0.3 (`2d1bd6e`) +- **deploy:** + - **android:** + - add keystore option for android signing (`8e172b0`) + - add request_install_packages option (`197e695`) + - download bundletool on demand (`fe1ab29`) + - download protoc on first deploy (`05fa2e5`) +- **platform:** + - **macos:** + - add basic macos debugging support (`bc77edb`) +- **ssl:** + - use mbedtls instead of axtls (`97d1a93`) + - support multiple ssl providers (`92a8392`) +- **file:** + - **android:** + - add method to create external storage directory (`dcf97fa`) +- **image:** + - add memory saving option for jpeg loading (`9bbf62c`) + - make freeTexture public (`1c6186b`) + - implement hashCode (`06ec18c`) +- **window:** + - add safe area inset support (`fa40948`) + - **android:** + - implement getSafeAreaInsets (`423110e`) +- **simulator:** + - add safeAreaInsets simulation support (`54f4079`) +- **httpstream:** + - add default deflate and gzip support (`1719ed7`) + - pick socket factory automatically (`cd9c4dd`) +- **settings:** + - add device unique identifier (`e60eba7`) +- **json:** + - add inputstream parsing support (`0d86d55`) + - use Reader for character encoding (`6a17b4b`) +- **socket:** + - **posix:** + - add ipv6 resolution support for posix (`83ea772`) +- **gps:** + - **android:** + - add ephemerides data operations (`c52851b`) +- **camera:** + - **android:** + - use CameraX (`2725655`) + - add player and recorder video modes (`3a1f035`) + - add getVideoResolutions method (`86d54f9`) + - rework VideoCaptureActivity with CameraX (`989c489`) + - add bitrate field for video recording (`377c3fd`) + - add pinch-to-zoom support (`98ca2e5`) + - add photo and preview to video capture (`1eeb3c2`) + - add flash and camera switch support (`3fe7be0`) + - add timer for video recording (`a510174`) + - add picture, video, and full modes (`1eb3a9d`) +- **keyboard:** + - **android:** + - smooth slide animation on sip close (`15593ff`) +- **uicolors:** + - add unsafeAreaColor field (`efdc797`) +- **gfx:** + - **macos:** + - add software rendering support (`cb95f37`) +- **filepicker:** + - **ios:** + - add native file selector (`d46c031`) +- **math:** + - implement Math.hypot (`7e0beb5`) +- **changelog:** + - add changelog generation scripts (`aa81dd2`) + +### Bug Fixes + +- **xcode:** + - **ios:** + - fix xcode configuration (`3b64bdb`) +- **script:** + - **ios:** + - fix typo with empty space (`325353f`) + - fix xcode path and target detection (`148fd6d`) + - **android:** + - stop checking includeSms in script (`9caaf13`) +- **socket:** + - **android:** + - check fd validity before closing (`413fc94`) + - **posix:** + - use poll for socket io monitoring (`05ecf76`) + - shutdown socket before close (`710fb6b`) + - fix open timeout on ios (`150ee08`) + - leave address info flags empty (`9b0eb46`) + - use AF_UNSPEC instead of PF_UNSPEC (`c22f7fe`) + - keep ipv4 dns resolution fallback (`e17d6dc`) + - improve nonblocking socket connect timeout handling (`18e3b97`) +- **launcher:** + - **android:** + - resume event handling after activity readiness (`1aa6274`) +- **camera:** + - **android:** + - use ContentResolver for file handling (`346bbea`) + - fix rotation support for custom camera (`41811b0`) + - fix orientation detection for custom camera (`0e15028`) + - apply auto rotation to gallery camera images (`2ab20e8`) + - avoid resizing image during auto rotation (`eac99c3`) + - prevent NO_COPY from overwriting last picture (`ca696fe`) + - require RECORD_AUDIO for camera (`ab29d28`) + - fix image orientation (`3277016`) + - avoid setTargetAspectRatio with setTargetResolution (`489b890`) + - fix picture orientation (`a2a57cc`) + - migrate getSupportedResolutions to Camera2 API (`707b843`) + - fix resolution handling in custom and picture modes (`4469019`) + - adjust to safe area insets (`8335c6e`) +- **permissions:** + - **android:** + - require storage permission on api level 30 (`da8a3de`) + - request file manage permission from manifest (`af48e5d`) + - request manage_external_storage only on android 10+ (`b558bf3`) + - request manage external storage on android 11+ (`624b87c`) + - use media permissions on android 13 and above (`6a729e8`) + - **ios:** + - add missing location usage permission (`1293896`) +- **build:** + - **android:** + - disable resource optimization (`bab4a47`) + - wrap paths in double quotes (`e130c5a`) + - fix pre and post build task execution (`1f44ac5`) + - fix zlib-ng build (`9012f2b`) + - **ios:** + - fix runpatch search paths for libswiftcore (`46fa390`) + - use correct xcode signing configuration (`479cd64`) + - fix xcode build (`d2c887e`) + - silence old warnings treated as errors (`57f870b`) +- **loader:** + - **android:** + - fix native library loading for aab distribution (`c49c4ca`) + - avoid invalid BuildConfig application id at runtime (`0c51aa9`) + - guard manage_external_storage permission request (`cb0076e`) + - avoid restricted telephony identifier access on Q+ (`d351fb9`) +- **deploy:** + - **android:** + - fix keystore path resolution for apk signing (`ff9f219`) + - fix removal of android permissions (`60233dd`) + - avoid race on pipe initialization (`9d6cb4c`) + - fix tcz splitting at class limit (`4240f54`) + - fix dist vm path discovery (`0a86f01`) + - improve host system detection for protoc (`f32c9d9`) +- **io:** + - prevent overflow on large inputs (`0494ae5`) + - support encoding correctly (`7b8bbe7`) + - make read block correctly (`2e799ba`) + - validate args for IOOBE (`3131a42`) +- **ssl:** + - restore simulator sslsocket support (`ec316ef`) + - enable timeout for sslsocket io (`479da73`) + - avoid reporting empty reads as eof (`befcfb6`) + - use native startHandshake in sslsocket (`d9057a3`) + - disable sslsocket timeout support (`d4dd7d8`) + - handle eof correctly and throw on read error (`81e6dd6`) + - fix read return value on close notify (`62e1009`) + - fix eof detection on read (`1900075`) + - **wince:** + - disables mbedtls on wince (`7c80f7c`) + - **posix:** + - avoid double close on unix (`a9e849f`) +- **file:** + - **android:** + - handle eperm as access denial (`601da81`) +- **firebase:** + - **ios:** + - disable firebase temporarily (`790f7c0`) + - disable firebase temporarily due to api changes (`5652eb6`) +- **bluetooth:** + - **android:** + - add bluetooth runtime permission request on 12+ (`a5589cb`) +- **sqlite:** + - return the correct sqlite version value (`48f7ad2`) + - enable use of sqlite-see for crypto support (`c739613`) + - fix retrieval of key for see activation (`6c707a6`) + - **wince:** + - fix wince build (`78ba44e`) +- **gps:** + - **android:** + - restore gps support (`234c078`) + - **ios:** + - fix gps lastFix time on ios (`191d0e2`) +- **png:** + - **windows:** + - fix crash from png stack corruption (`aef94ca`) + - fix support for c89 compilers (`20abf02`) + - **wince:** + - patch PNG_ABORT declaration for wince (`17e0f02`) + - patch files for wince errno support (`8d28604`) +- **jpeg:** + - fix exact scale match for scaled jpeg reads (`66e29aa`) +- **gfx:** + - **ios:** + - stop changing frame bounds for margins (`014881c`) + - fix shifted color screen glitch (`7b04c56`) +- **window:** + - fix Menu object usage (`2442759`) + - improve safeAreaInsets support (`8d903a7`) +- **scanner:** + - **android:** + - improve reading speed on android (`e6efeba`) + - upgrade scanner dependency to 3.5.0 (`de77e71`) +- **utf8:** + - use replacement character on failure (`0d3844c`) +- **ui:** + - fix occasional npe (`5ef55b5`) + - adjust popup position and size (`3b3882e`) +- **insets:** + - **android:** + - fix sip detection with insets (`e314e11`) + - fix usable area detection (`0423729`) +- **keyboard:** + - **android:** + - restore keyboard support with skia (`f132afd`) + - smooth slide animation on sip open (`52aaab3`) + - fix sip slide animation (`75d35e2`) + - fix keyboard handling on samsung (`3b59cbf`) +- **multiedit:** + - fix scroll glitch with sip (`5eabeea`) +- **scrollcontainer:** + - pair SCROLL_END with SCROLL_START (`d77866f`) +- **interpreter:** + - **android:** + - avoid ndk 22 performance loss (`2349086`) +- **sys:** + - use Math.hypot in Convert.getDistancePoint2Rect (`f15d65f`) +- **omm:** + - initialize OMM list heads to prevent invalid image traversal (`71facca`) +- **image:** + - guard texture state against partially initialized image objects (`0df85e8`) +- **runtime:** + - **android:** + - use pointer-safe format in surface null debug log (`6568c69`) +- **surface:** + - **android:** + - bootstrap surface earlier to avoid black screen on resume (`161e275`) +- **misc:** + - **ios:** + - keep pod archive out of static tcvm link phase (`af2800b`) + - generate a valid Xcode frameworks phase patch (`2eec588`) + - remove obsolete firebase instance id pod (`6b57534`) + - correct xcode project references (`5e1f83c`) + - inherit pod search paths for tcvm (`9bc02f2`) + +### Performance Improvements + +- **sqlite:** + - downgrade sqlite to 3.32.3 (`6bcf3b0`) +- **image:** + - let gpu handle image scaling when possible (`4d01b9e`) + - reduce createJpg output size (`0ce69d8`) +- **jpeg:** + - **posix:** + - use mmap in getJpegBestFit (`e09a624`) +- **gfx:** + - **ios:** + - avoid recreating opengl context on rotation (`4162e4d`) +- **simulator:** + - reduce cost when scaled (`87b66e7`) +- **vm:** + - **macos:** + - increase default memory allocation chunk size (`92a507f`) +- **json:** + - increase JSONTokener initial size (`d36da93`) + - optimize JSONTokener character reading (`2569780`) +- **scanner:** + - **android:** + - improve scanning speed (`f62b324`) +- **ninepatch:** + - avoid recreating parts for the same image (`5155afd`) + - add cache to avoid recreating parts (`98d2361`) +- **button:** + - skip onBoundsChanged when text length is unchanged (`a131268`) +- **ui:** + - use freeTexture to release discarded resources sooner (`372d397`) +- **scrollposition:** + - avoid recreating ninepatch parts during paint (`be6b19f`) + - reuse ninepatch parts across instances (`e9476e5`) +- **i18n:** + - load maps on demand (`98f824d`) +- **sdk:** + - reduce overhead in InputStreamReader read methods (`f13dea5`) + +### Refactors + +- **file:** + - **posix:** + - move duplicate code into define (`0343411`) +- **zlib:** + - fix zlib include usage (`cd4cd8f`) +- **png:** + - use public api instead of internal headers (`9e8c808`) +- **sqlite:** + - clean sqlite integration (`51cca0a`) +- **c:** + - fix INT32_MAX redefinition warnings (`d920fc8`) +- **misc:** + - **ios:** + - remove unused taskbar and bounds helpers (`736e251`) + - **android:** + - rename enum to avoid clash (`081fa85`) +- **cmake:** + - set env var by target (`146d955`) + - use TARGET_WINCE for readability (`25a9288`) +- **camera:** + - **android:** + - refactor adjustToSafeArea (`4bbab73`) +- **ninepatch:** + - remove duplicate code (`e676ce8`) + - group methods with the same name (`77e0fa1`) + - make Parts immutable (`1fb5003`) + - make final load method private (`2dd4ca9`) +- **scrollposition:** + - rename fields for readability (`5509c11`) +- **json:** + - replace StringReader4D with StringReader in JSONTokener (`ce881c8`) +- **tcvm:** + - move architecture size macros out of tcvm.h (`656f93e`) + +### Documentation + +- **contributing:** + - update commit message guidelines (`f475e6a`) + - simplify commit message rules (`08f0c24`) +- **git:** + - update commit message template (`1ec7814`) +- **changelog:** + - update commit message template (`411475f`) + - update changelog generator (`5e99f31`) +- **agents:** + - add repository agent guidelines (`ab63a39`) + +### Build + +- **vs2008:** + - update output path (`587d1c2`) + - move build files to project root (`c604e88`) +- **script:** + - add scripts for building and packaging (`66ffb1a`) + - **ios:** + - warn about mbedtls dependencies in xcode (`c83201e`) + - update script to build ios from github (`7d6d564`) +- **misc:** + - **android:** + - remove jcenter repository (`83fc4aa`) + - make SingleApk the default build (`b8e5b9a`) + - move build files to project root (`e87f0af`) + - raise compile and target sdk to 32 (`3d3188c`) + - raise compile and target sdk to 33 (`de5366e`) + - upgrade agp to 7.4.2 (`fc41de8`) + - upgrade agp to 8 and remove unsupported flag (`ff45f9b`) + - upgrade agp to 8.1.0 (`8b96176`) + - upgrade agp to 8.2.2 (`8fd9b77`) + - raise compile and target sdk to 34 (`fe2264d`) + - raise compile and target sdk to 35 (`1033325`) + - upgrade agp to 8.3.0 (`1df6c73`) + - upgrade agp to 8.4.0 (`57e50ae`) + - upgrade agp to 8.5.1 (`96de1d8`) + - enable minify with proguard keep rules (`cac1d69`) + - upgrade agp to 8.5.2 (`eb36de0`) + - upgrade agp to 8.6.0 (`dec6133`) + - fetch skia prebuilts before native builds (`5f0a9cd`) + - reuse skia abi list for native build (`50c197b`) + - generate only arm64 native library (`068378a`) + - fetch Skia dev bundle for native headers (`85b4ff3`) + - restrict TotalCross maven repository (`cea381d`) + - **ios:** + - update mobile provision (`5a1c289`) + - move ios build files to project root (`a10e60e`) + - raise minimum support to ios 12.1 and drop armv7 (`7b90804`) + - fix simulator build on apple silicon (`2e615ae`) + - automate static library links in generated Xcode project (`e2faa59`) + - normalize generated project name casing (`95ed417`) + - align deployment target to 12.1 (`f4e292c`) + - **linux:** + - lower optimization for GoogleMaps workaround (`bdbdb77`) + - use madler zlib for legacy toolchain compatibility (`a0d9e55`) +- **toolchain:** + - **android:** + - update build tools and android dependencies (`f605f79`) + - raise sdk to 31 and ndk to 25.0.8775105 lts (`72ce2e5`) + - update ndk and gradle tools versions (`e5e62a6`) + - avoid ndk 22 and newer for performance (`7b1ca7b`) + - use new toolchain for release builds (`963d910`) + - upgrade ndk to 22.1.7171670 (`da4f74e`) + - upgrade ndk to 23.2.8568313 (`46f3257`) + - upgrade ndk to 24.0.8215888 (`6e05266`) + - upgrade ndk to 25.2.9519653 (`6f95257`) + - upgrade ndk to 26.3.11579264 (`e1cddad`) + - upgrade ndk to 28.2.13676358 (`f4f097c`) +- **deploy:** + - **android:** + - remove includeSms option (`541513c`) + - generate aab and apk files (`18643d5`) +- **dependencies:** + - **android:** + - update android dependencies (`344dbb1`) + - update firebase dependencies (`89bf6b9`) + - upgrade google-services plugin (`19ded5e`) + - upgrade maps dependency (`61f9b17`) + - upgrade location dependency (`153c28f`) + - **ios:** + - raise FirebaseMessaging version to 10.8.0 (`6894901`) +- **scanner:** + - **ios:** + - remove scandit support (`08b8303`) + - **wince:** + - disable scanner library build on wince (`455d7cd`) + - **android:** + - upgrade zxing to 4.3.0 (`a1fccd1`) +- **cocoapods:** + - **ios:** + - raise podfile minimum ios version to 11 (`d1fae5e`) + - enable use_modular_headers for static library modules (`c712ad1`) +- **xcode:** + - **ios:** + - update xcode files for xcode 14.3 (14E22B) (`49cc49d`) +- **cmake:** + - **ios:** + - set xcode compiler attributes (`3448885`) + - target ios 13 in all configurations (`136e184`) + - fix xcode dependency handling in cmake (`6b8cc68`) + - remove unnecessary install section (`6f794c4`) + - move third-party dependencies into separate files (`d22087f`) + - standardize third-party property names (`6b43f0c`) + - use shallow clone to speed up builds (`ab50ee9`) + - externalize skia artifacts and auto-fetch prebuilts (`690e2ca`) + - require CMake 3.11 for FetchContent (`39f46c5`) + - require CMake 3.11 across TCVM subprojects (`44dfcb3`) + - use prebuilt sqlite artifacts (`be4e09a`) + - support private sqlite artifacts (`e9c7103`) + - use prebuilt mbedtls artifacts (`2dac5c4`) +- **zlib:** + - replace zlib with zlib-ng (`22719eb`) + - add option for madler zlib (`775d7f9`) + - enable PIC for shared TCVM linking (`afa9ddb`) +- **png:** + - upgrade libpng to 1.6.40 (`7ad735b`) + - use the active zlib implementation for libpng (`dd389c3`) + - disable png write support (`e7637b4`) + - patch files only for wince (`cd1ef2d`) + - upgrade libpng to 1.6.48 (`48f52a3`) + - enable PIC for shared TCVM linking (`f49ba61`) + - expose fetched zlib to libpng on Windows (`7ff71cc`) + - resolve zlibstatic alias before exporting ZLIB target (`c58a1d4`) + - **windows:** + - use libpng 1.6.40 for MSVC builds (`5110a4f`) +- **debug:** + - **android:** + - add debug guards for android debugging (`3b8043a`) +- **ads:** + - **android:** + - disable ads support temporarily (`e58c7e2`) +- **zlib-ng:** + - upgrade to 2.1.6 and fix for xcode and clang build (`93721b0`) + - disable shared library builds in embedded setup (`86901ed`) + - resolve aliased static target before setting properties (`ee6a5ab`) + - resolve aliased zlibstatic target before setting properties (`ae0055e`) +- **mbedtls:** + - upgrade to 3.5.2 and fix for xcode and clang build (`46be2b8`) + - enable PIC for shared TCVM linking (`634390d`) + - link bcrypt on Windows (`d2765a2`) +- **camera:** + - **android:** + - upgrade CameraX to 1.5.2 (`b23dc13`) +- **vm:** + - force libc allocators when building with Skia (`6796482`) +- **sqlite3:** + - enable PIC for shared TCVM linking (`9f50007`) +- **ci:** + - drop legacy arm64 workarounds after native runner switch (`3f960b8`) + - add experimental linux arm32v7 cross-build job (`2ee14b5`) + - fetch sqlite before linux docker builds (`c192ab9`) + - add versioned linux docker image workflow (`58f388d`) +- **third-party:** + - simplify embedded zlib integration hooks (`f7999bd`) +- **cross:** + - declare NEON FPU in linux arm32v7 toolchain (`2e847b0`) +- **skia:** + - retry transient artifact download failures (`413999a`) +- **sdk:** + - package runtime dependencies in dist libs (`0147677`) +- **package:** + - use packaged SDK artifact as package base (`4295dde`) + - match SDK version jar exactly (`6978719`) +- **sqlite:** + - log resolved artifact variant (`056c84a`) + - support configurable SQLite variants (`977e84c`) + - fix release asset id lookup (`b339107`) + - disable curl globbing in fetch script (`8f6c0e8`) + - harden release asset download (`d32a448`) + - keep fetch script compatible with older curl (`5b5c3eb`) + - validate fetch release parameters (`56cddff`) + - ignore empty fetch environment values (`52b9b56`) +- **deps:** + - consume native deps from depot tools (`2970716`) + +## [7.2.0] - 2026-02-20 + +PLACEHOLDER FOR CHANGELOG + ## [7.1.0] - 2021-04-23 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f83e6e9367..49b70a5c5a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ - [Feedback](#feedback) - [Code](#code) - [Environment](#environment) -- [Commiting](#commiting) + - [Commit Message Guidelines](#commit-message-guidelines) - [Why all these rules?](#why-all-these-rules) - [Submitting a pull request](#submitting-a-pull-request) @@ -121,62 +121,217 @@ To build TotalCross for each system below, you will need:: With these prerequisites, you may need to clone this repository, as well as have a ready SDK and a [sample code](https://github.com/TotalCross/hello-world) to get started. -## Commiting +### Commit Message Guidelines -Our commit style: +To keep the project history clear, consistent, and easy to maintain, all commits must follow the rules below.
+This project follows a Conventional Commits–inspired format, +using project subsystems as scopes.
+> [!WARNING] +> Commit messages are automatically validated by GitHub Actions. +#### Commit message format ``` -: +([,]): short description +!([,]): short description +([,])!: short description -[optional body] +optional body -[optional footer] +optional footer ``` -About our standard: -- The header (first line) is the only mandatory part of the commit message; -- The body and footer are both optional but its use is highly encouraged; -> The blank line separating the header from the body is critical, same for footer. -- The header should contains: - - A type: - - Must be lowercase; - - A subject: - - Must be non capitalized; - - Must omit any trailing punctuation. - - Must be limited to 80 characters or less (length = type + subject); -- The body: - - Must have a leading blank line; - - Each line must be limited to 80 characters or less. -- The footer: - - Must have a leading blank line; - - Each line must be limited to 80 characters or less; - - If needed, reference to issues and pull requests must be made here in the last line. +**Example** +``` +fix(socket,posix): handle eof correctly + +Handle eof correctly in the posix socket path to avoid spurious +connection failures during shutdown. +``` + +#### Commit title rules +The first line of the commit message (the title): + +- Must start with a lowercase letter +- Must contain at least 3 words (to avoid vague titles such as "fix bug") +- Must be at most 80 characters long +- Must use the imperative mood (e.g. fix, add, remove) +- Must follow the format ([,]): description +- Must not end with a period +- If a commit body is present, it must be separated from the title by a blank line. + +These rules are enforced automatically by CI. + +#### Commit types + +Use the following commit types to describe the primary intent of the change. +Choose the most specific type that matches the nature of the change. + +> [!IMPORTANT] +> Each commit should represent a single logical change. +> Avoid mixing refactors, fixes, and formatting in the same commit. + +| Type | Description | +| ---------- | ---------------------------------------------------------------------------------- | +| `fix` | Bug fixes where the primary intent is correctness. | +| `feat` | New user-facing or public functionality. | +| `refactor` | Code restructuring without changing external behavior. | +| `perf` | Performance improvements without functional changes. | +| `style` | Formatting, whitespace, or style-only changes without behavior changes. | +| `test` | Adding, updating, or fixing tests. | +| `docs` | Documentation, examples, comments, or changelog updates. | +| `build` | Build system, dependency, packaging, or release configuration changes. | +| `ci` | GitHub Actions or other continuous integration configuration changes. | +| `chore` | Maintenance tasks that do not fit a more specific type. | +| `revert` | Reverts one or more previous commits. | + +> [!TIP] +> Use `fix` for correctness bugs, even if they incidentally improve performance. +> Prefer expressing the affected area in the scope rather than inventing new types. + +#### Scope and platform + +Inside the parentheses, qualifiers are positional and must follow this order: + +1. `scope` (required): the primary subsystem or area +2. `platform` (optional): the operating system or target environment + +Examples: +- `fix(vm,linux): ...` +- `feat(sdk,android): ...` +- `build(cmake,windows): ...` + +##### Common scopes + +Scopes should be short, stable subsystem names. Examples: +- `vm` +- `runtime` +- `gc` +- `sdk` +- `compiler` +- `bytecode` +- `socket` +- `ssl` +- `camera` +- `ui` +- `json` +- `jpeg` +- `cmake` +- `packager` +- `deploy` +- `toolchain` + +##### Common platforms + +Use a platform qualifier when the change is mainly specific to that target: + +| Platform | Description | +| --------- | -------------------------------------------------------------------------------------------- | +| `android` | Android-specific code, packaging, lifecycle, NDK/JNI integration. | +| `ios` | iOS-specific code, packaging, native bindings, platform integration. | +| `macos` | macOS-specific changes not tied to a single macOS architecture. | +| `windows` | Windows-specific changes not tied to a single Windows target. | +| `wince` | Windows CE-specific behavior, compatibility, or toolchain changes. | +| `winmo` | Windows Mobile-specific behavior, integration, or packaging. | +| `linux` | Linux-specific behavior not tied to a single Linux architecture. | +| `posix` | Shared POSIX behavior spanning Linux, macOS, Android, iOS, or other POSIX-like targets. | + +##### Architectures + +Architecture is not part of the commit title format. When a change is relevant +to a specific architecture, mention it in the commit body instead, such as +`arm64`, `x86`, or `x64`. + +**Qualified commit examples** +``` +fix(vm): handle null class loader +fix(socket,posix): handle eof correctly +feat(sdk,android): add video resolution query +refactor(gc): simplify mark queue handling +build(cmake,windows): fix static png linking +build(deploy,android): add custom keystore support +revert(sdk): restore legacy deploy option +``` +> [!IMPORTANT] +> The first qualifier must always be the subsystem scope. +> Use the optional platform qualifier only when it makes the history +> materially clearer. -You also should follow these general guidelines when committing: +> Prefer multiple small commits over a single broad one when platform or +> architecture behavior differs. Mention architecture in the body when it +> materially affects the change. -- Use the present tense ("remove feature" not "removed feature"); -- Use the imperative mood ("resize object to..." not "resizes object to..."); -- Try to answer the following questions: - - What is the reason for this change? - - What side effects (if any) does this change may have? +#### Breaking changes -Example of commit message: +Use `!` in the commit title when a change breaks compatibility: + +``` +feat(sdk)!: remove legacy deploy option +feat!(sdk): remove legacy deploy option ``` -doc: summarize changes in around 80 characters or less -More detailed explanatory text, if necessary. Wrap it to about 80 -characters per line. In some contexts, the first line is treated as -the subject of the commit and the rest of the text as the body. The -blank line separating the header from the body is critical (unless -you omit the body entirely); various tools like `log`, `shortlog` -and. -Explain the problem that this commit is solving. Focus on why you -are making this change as opposed to how (the code explains that). -Are there side effects or other unintuitive consequences of this -change? Here's the place to explain them. +When the impact or migration path needs explanation, add a footer: + +``` +BREAKING CHANGE: legacy deploy option is no longer supported. +``` -Closes #123 +Keep the breaking-change footer separated from the body by a blank line. + +#### Commit body guidelines +The commit body is optional, but encouraged when: +- the change is non-trivial +- the reasoning is not obvious from the diff +- there are trade-offs or side effects + +Guidelines: +- Wrap lines at 80 characters +- Explain why the change was made, not just what changed +- Mention architecture here when relevant (for example, `arm64`, `x86`, `x64`) +- For `revert` commits, reference the commit hash or hashes being reverted +- Reference issues when applicable (e.g. closes #123) + +> [!TIP] +> Use the commit body to capture reasoning, constraints, +> and rejected alternatives when relevant. + +Language and consistency +- All commit messages must be written in English +- Use technical and precise language +- Avoid vague titles such as: + ``` + fix bug + update files + changes + ``` + +Example body ``` +Fixes an invalid GC mark when objects are promoted +during minor collection. + +This prevents rescanning promoted objects and avoids +invalid memory access under heavy allocation. +``` + +#### Commit template (recommended) +Developers are encouraged to use the provided commit message template to avoid CI failures and keep consistency: +``` +([,]): short description (lowercase, ≤ 80 chars) + +Optional body: +- explain what changed +- explain why it changed +- reference issues if applicable + +Optional footer: +BREAKING CHANGE: explain compatibility impact and migration +``` +You can enable it locally with: +``` +git config commit.template .gitmessage.txt +``` + +Following these guidelines ensures a clean, searchable history and helps maintain the long-term stability of the TotalCross codebase. ### Why all these rules? diff --git a/LitebaseSDK/.gitignore b/LitebaseSDK/.gitignore deleted file mode 100644 index 481261b3d1..0000000000 --- a/LitebaseSDK/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -/*.db -/*.dbo -/*.idk -/*.idr -/*.pdb -/temp* - -temp/ -output/ -/dist diff --git a/LitebaseSDK/License.txt b/LitebaseSDK/License.txt deleted file mode 100644 index 38991f462e..0000000000 --- a/LitebaseSDK/License.txt +++ /dev/null @@ -1,168 +0,0 @@ -THIS LICENSE APPLIES TO THE JAVA FILES BUT NOT FOR THE BINARY FILES - - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. \ No newline at end of file diff --git a/LitebaseSDK/build.xml b/LitebaseSDK/build.xml deleted file mode 100644 index 9dc7617c07..0000000000 --- a/LitebaseSDK/build.xml +++ /dev/null @@ -1,336 +0,0 @@ - - - - - - - - - - - - This buildfile is used to build the Litebase java based components. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LitebaseSDK/builders/_sfx.diz b/LitebaseSDK/builders/_sfx.diz deleted file mode 100644 index e0b067a6b2..0000000000 --- a/LitebaseSDK/builders/_sfx.diz +++ /dev/null @@ -1,40 +0,0 @@ -Title=Litebase @VERSION@ -Path=c:\Litebase -Overwrite=1 -Setup=setup.bat - -Text -{ -Welcome to the installation of Litebase. - -* WARNING * WARNING * -Don't forget to read the "Litebase Companion.pdf file" in the docs folder. -There are very important informations about the new Database formats and -migration to this format. -} - -License -{ - TO INSTALL LITEBASE YOU MUST AGREE WITH THIS LICENSE - - English (Portugues logo abaixo) - - -The Litebase SDK (Softwate Development Kit)is NOT part of SuperWaba SDK, it - is NOT an open source software and can NOT be distributed to third parties. - - -The Litebase executable files are NOT part of SuperWaba SDK and are NOT - open source softwares, however the CUSTOMER can distribute these files among - its customers for commercial purposes. This distribution right is RESTRICTED - to versions 1.X. Superior versions (e.g. 2.X or 3.X) are subjected to other - agreements. - - Portuguese - - -O Kit de Desenvolvimento do Litebase NO parte do SDK SuperWaba, NO - um software livre e NO pode ser distribuido a terceiros - - -Os arquivos executveis do Litebase NO so softwares livres, mas podem ser - distribuidos livremente e sem custo adicional. Contudo este direito est - limitado as verses 1.X, verses superiores esto sujeitas a licenas - diferentes -} diff --git a/LitebaseSDK/builders/android/apps/litebase/.gitignore b/LitebaseSDK/builders/android/apps/litebase/.gitignore deleted file mode 100644 index 38b671d360..0000000000 --- a/LitebaseSDK/builders/android/apps/litebase/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/bin -/gen -/deploy -/libs diff --git a/LitebaseSDK/builders/android/apps/litebase/AndroidManifest.xml b/LitebaseSDK/builders/android/apps/litebase/AndroidManifest.xml deleted file mode 100644 index 5e0a15b02d..0000000000 --- a/LitebaseSDK/builders/android/apps/litebase/AndroidManifest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/LitebaseSDK/builders/android/apps/litebase/assets/tcfiles.zip b/LitebaseSDK/builders/android/apps/litebase/assets/tcfiles.zip deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/LitebaseSDK/builders/android/apps/litebase/build.properties b/LitebaseSDK/builders/android/apps/litebase/build.properties deleted file mode 100644 index f2ee51ddef..0000000000 --- a/LitebaseSDK/builders/android/apps/litebase/build.properties +++ /dev/null @@ -1,27 +0,0 @@ -# This file is used to override default values used by the Ant build system. -# -# This file must be checked in Version Control Systems, as it is -# integral to the build system of your project. - -# This file is only used by the Ant script. - -# You can use this to override default values such as -# 'source.dir' for the location of your java source folder and -# 'out.dir' for the location of your output folder. -out.release.file.name=Litebase.apk -java.encoding=iso-8859-1 -out.dir=deploy -source.dir=deploy/src -gen.dir=deploy/gen -resource.dir=res -assets.dir=deploy/assets - -# You can also use it define how the release builds are signed by declaring -# the following properties: -# 'key.store' for the location of your keystore and -# 'key.alias' for the name of the key to use. -# The password will be asked during the build when you use the 'release' target. -key.store.password=@ndroid$w -key.store=P:/gitrepo/TotalCross/TotalCrossSDK/etc/security/tcandroidkey.keystore -key.alias=tcandroidkey -key.alias.password=@ndroidsw diff --git a/LitebaseSDK/builders/android/apps/litebase/build.xml b/LitebaseSDK/builders/android/apps/litebase/build.xml deleted file mode 100644 index 5b74ec5836..0000000000 --- a/LitebaseSDK/builders/android/apps/litebase/build.xml +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ************ Packaging resources ************ - - - - - - - - - - - - - - - diff --git a/LitebaseSDK/builders/android/apps/litebase/local.properties b/LitebaseSDK/builders/android/apps/litebase/local.properties deleted file mode 100644 index 27eee3c27f..0000000000 --- a/LitebaseSDK/builders/android/apps/litebase/local.properties +++ /dev/null @@ -1,10 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must *NOT* be checked in Version Control Systems, -# as it contains information specific to your local configuration. - -# location of the SDK. This is only used by Ant -# For customization when using a Version Control System, please read the -# header note. -sdk.dir=P:\\android-sdk-windows diff --git a/LitebaseSDK/builders/android/apps/litebase/res/drawable-hdpi/icon.png b/LitebaseSDK/builders/android/apps/litebase/res/drawable-hdpi/icon.png deleted file mode 100644 index d265d5687f..0000000000 Binary files a/LitebaseSDK/builders/android/apps/litebase/res/drawable-hdpi/icon.png and /dev/null differ diff --git a/LitebaseSDK/builders/android/apps/litebase/run_addr2line.sh b/LitebaseSDK/builders/android/apps/litebase/run_addr2line.sh deleted file mode 100644 index 7a0b43a187..0000000000 --- a/LitebaseSDK/builders/android/apps/litebase/run_addr2line.sh +++ /dev/null @@ -1,4 +0,0 @@ -cd /cygdrive/p/gitrepo/Litebase/LitebaseSDK/builders/android/apps/litebase/libs/armeabi -echo Digite os valores hexa de PC comecando com 0x. digite control+c para fechar -echo Feche a janela antes de compilar o programa, pois o .so fica BLOQUEADO. -addr2line -f -e liblitebase.so \ No newline at end of file diff --git a/LitebaseSDK/builders/build.xml b/LitebaseSDK/builders/build.xml deleted file mode 100644 index b2f546ab42..0000000000 --- a/LitebaseSDK/builders/build.xml +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - This file is used to deploy the Litebase C based components. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LitebaseSDK/builders/gcc/AUTHORS b/LitebaseSDK/builders/gcc/AUTHORS deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/LitebaseSDK/builders/gcc/COPYING b/LitebaseSDK/builders/gcc/COPYING deleted file mode 100644 index 88f7aad29f..0000000000 --- a/LitebaseSDK/builders/gcc/COPYING +++ /dev/null @@ -1,121 +0,0 @@ -GNU LESSER GENERAL PUBLIC LICENSE - -Version 2.1, February 1999 - -Copyright (C) 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] - -Preamble - -The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. - -This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. - -When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. - -To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. - -For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. - -We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. - -To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. - -Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. - -Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. - -When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. - -We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. - -For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. - -In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. - -Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. - -The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. - -TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". -A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. - -The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) - -"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. - -Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - -1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. -You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - -2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: -a) The modified work must itself be a software library. -b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. -c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. -d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. -(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. - -3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. -Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. - -This option is useful when you wish to copy part of the code of the Library into a program that is not a library. - -4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. -If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. - -5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. -However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. - -When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. - -If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) - -Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - -6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. -You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: - -a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) -b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. -c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. -d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. -e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. -For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. - -It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. - -7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: -a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. -b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. -8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. -9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. -10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. -11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. -If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. - -This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - -12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. -13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. - -14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. -NO WARRANTY - -15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. -16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file diff --git a/LitebaseSDK/builders/gcc/ChangeLog b/LitebaseSDK/builders/gcc/ChangeLog deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/LitebaseSDK/builders/gcc/INSTALL b/LitebaseSDK/builders/gcc/INSTALL deleted file mode 100644 index 23e5f25d0e..0000000000 --- a/LitebaseSDK/builders/gcc/INSTALL +++ /dev/null @@ -1,236 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free -Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - -Basic Installation -================== - -These are generic installation instructions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. (Caching is -disabled by default to prevent problems with accidental use of stale -cache files.) - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You only need -`configure.ac' if you want to change it or regenerate `configure' using -a newer version of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. If you're - using `csh' on an old version of System V, you might need to type - `sh ./configure' instead to prevent `csh' from trying to execute - `configure' itself. - - Running `configure' takes awhile. While running, it prints some - messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - -You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you must use a version of `make' that -supports the `VPATH' variable, such as GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - If you have to use a `make' that does not support the `VPATH' -variable, you have to compile the package for one architecture at a -time in the source code directory. After you have installed the -package for one architecture, use `make distclean' before reconfiguring -for another architecture. - -Installation Names -================== - -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - -Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). Here is a another example: - - /bin/bash ./configure CONFIG_SHELL=/bin/bash - -Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent -configuration-related scripts to be executed by `/bin/bash'. - -`configure' Invocation -====================== - -`configure' recognizes the following options to control how it operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/LitebaseSDK/builders/gcc/Makefile.am b/LitebaseSDK/builders/gcc/Makefile.am deleted file mode 100644 index 24fc376375..0000000000 --- a/LitebaseSDK/builders/gcc/Makefile.am +++ /dev/null @@ -1,84 +0,0 @@ -############################################################################### -# Makefile for the Superwaba VM. -# @configure_input@ -# $Id: Makefile.am,v 1.11.4.1.2.1.4.30 2011-01-26 19:44:55 juliana Exp $ -############################################################################### - -sourcedir = ${srcdir}/../../src/native -tcvmdir = ${srcdir}/../../../../TotalCross/TotalCrossVM/src - -AM_CFLAGS = -DTHEOS -DPOSIX -D_REENTRANT -DLITEBASE -Wall -Wunused-function -Wno-import -DLB_EXPORTS -fno-strict-aliasing -I${sourcedir} -I${sourcedir}/parser -I${tcvmdir}/util -I${tcvmdir}/tcvm -I${tcvmdir}/nm/io -I${tcvmdir}/nm/lang - -# uncomment for a debug build -# AM_CFLAGS += -I/opt/include/valgrind -g -O0 -DENABLE_TRACE - -#if LINUX -AM_CFLAGS += -I/usr/include/_directfb -#endif - -# enable through "--enable-demo" during "configure" -if DEMO_VERSION -AM_CFLAGS += -DENABLE_DEMO -endif - -if TESTSUITE -# enable through "--enable-testsuite" during "configure" -AM_CFLAGS += -DENABLE_TEST_SUITE -endif - -inst_LTLIBRARIES = libLitebase.la -instdir = /opt/totalcross - -Litebase_sources = \ - $(sourcedir)/lbFile.c \ - $(sourcedir)/PlainDB.c \ - $(sourcedir)/TCVMLib.c \ - $(sourcedir)/Litebase.c \ - $(sourcedir)/ResultSet.c \ - $(sourcedir)/Table.c \ - $(sourcedir)/NativeMethods.c \ - $(sourcedir)/UtilsLB.c \ - $(sourcedir)/Key.c \ - $(sourcedir)/Node.c \ - $(sourcedir)/Index.c \ - $(sourcedir)/SQLValue.c \ - $(sourcedir)/MarkBits.c \ - $(sourcedir)/MemoryFile.c \ - $(sourcedir)/NormalFile.c \ - $(sourcedir)/PreparedStatement.c \ - $(sourcedir)/LitebaseGlobals.c - -Parser_sources = \ - $(sourcedir)/parser/LitebaseLex.c \ - $(sourcedir)/parser/LitebaseMessage.c \ - $(sourcedir)/parser/LitebaseParser.c \ - $(sourcedir)/parser/SQLBooleanClause.c \ - $(sourcedir)/parser/SQLBooleanClauseTree.c \ - $(sourcedir)/parser/SQLColumnListClause.c \ - $(sourcedir)/parser/SQLDeleteStatement.c \ - $(sourcedir)/parser/SQLInsertStatement.c \ - $(sourcedir)/parser/SQLSelectStatement.c \ - $(sourcedir)/parser/SQLUpdateStatement.c - -Tests_sources = \ - $(tcvmdir)/tests/tc_testsuite.c - -libLitebase_la_SOURCES = ${Litebase_sources} ${Parser_sources} ${Tests_sources} - -libLitebase_la_LIBADD = ${AM_LDFLAGS} -lm - -if LINUX -OBJCEXT=c -endif - -if DARWIN -AM_OBJCFLAGS = ${AM_CFLAGS} -I${sourcedir}/util -I${sourcedir}/event -I${sourcedir}/nm/ui -I${sourcedir}/init -I${sourcedir}/tcvm -OBJCEXT=m -endif - -if DARWIN9 -codesign: all - arm-apple-darwin9-strip -S `find .libs/ -type f -name "libLitebase*dylib" -print` - CODESIGN_ALLOCATE=${DARWIN_ROOTDIR}/pre/bin/arm-apple-darwin9-codesign_allocate \ - ${TOTALCROSS_SDK_ROOT}/etc/tools/ldid/ldid.bin -S `find .libs/ -type f -name "libLitebase*dylib" -print` -endif diff --git a/LitebaseSDK/builders/gcc/NEWS b/LitebaseSDK/builders/gcc/NEWS deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/LitebaseSDK/builders/gcc/README b/LitebaseSDK/builders/gcc/README deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/LitebaseSDK/builders/gcc/autogen.sh b/LitebaseSDK/builders/gcc/autogen.sh deleted file mode 100644 index bd5e115e6b..0000000000 --- a/LitebaseSDK/builders/gcc/autogen.sh +++ /dev/null @@ -1,12 +0,0 @@ -#! /bin/sh - -echo "Regenerate the autoconf/automake files"; - -libtoolize --force --automake - -rm -f config.cache -rm -f config.log - -autoheader -autoconf -automake -a diff --git a/LitebaseSDK/builders/gcc/build.sh b/LitebaseSDK/builders/gcc/build.sh deleted file mode 100644 index 0bceb34118..0000000000 --- a/LitebaseSDK/builders/gcc/build.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/bash - -function display_help -{ - echo "`basename $0` arguments:" - echo " -demo build a demo version" - echo " -help this help message" - exit -} - -type="release" -noras="" -norasid="" -out_folder="ras" - -while [ $1 ]; -do - case "$1" in - -d|-demo) - type="demo" - out_folder="demo" - shift - ;; - -h|-help) - display_help # a function ;-) - # no shifting needed here, we'll quit! - exit - ;; - *) - echo "Error: Unknown option: $1" >&2 - exit 1 - ;; - esac -done - -export PKGNAME="LitebaseSDK" -cd $(dirname $0) -export BASEDIR=$(dirname $0) -export WORKSPACE=$(cd -- "../../../.." && pwd -P 2>/dev/null | pwd -P) -export SDK=$WORKSPACE/TotalCross/TotalCrossSDK -export REPONAME="Litebase" - -# generate configure if required -if [ ! -f $BASEDIR/configure ]; -then - cd $BASEDIR && chmod a+x autogen.sh && ./autogen.sh -fi - -# build -mkdir -p $BASEDIR/linux/$type -cd $BASEDIR/linux/$type -../../configure --enable-$type --with-sdk-prefix=$SDK -make clean -make -s -j $NUMBER_OF_PROCESSORS -cp -L .libs/libLitebase.so . diff --git a/LitebaseSDK/builders/gcc/configure.ac b/LitebaseSDK/builders/gcc/configure.ac deleted file mode 100644 index b1995610ae..0000000000 --- a/LitebaseSDK/builders/gcc/configure.ac +++ /dev/null @@ -1,248 +0,0 @@ -# -# Process this file with autoconf to produce a configure script. -# -# Copyright 2004 by Superwaba, Inc. -# -# This file is part of the TotalCross package. -# -# $Id: configure.ac,v 1.1.2.21 2011-03-02 21:05:33 guich Exp $ -# - -AC_INIT(../../src/native/Litebase.c) -AC_COPYRIGHT([ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only -]) - -SDK_ROOT=../../../ -AC_SUBST(SDK_ROOT) - -PACKAGE=LiteBase -AC_SUBST(PACKAGE) - -# +1 : ? : +1 == new interface that does not break old one -# +1 : ? : 0 == new interface that breaks old one -# ? : ? : 0 == no new interfaces, but breaks apps -# ? :+1 : ? == just some internal changes, nothing breaks but might work better -# CURRENT : REVISION : AGE - -#------------------------------------------------------------------------------ -# Version information -#------------------------------------------------------------------------------ -dnl version numbers -MAJOR_VERSION=${MAJOR_VERSION:-2} -AC_SUBST(MAJOR_VERSION) -MINOR_VERSION=${MINOR_VERSION:-5} -AC_SUBST(MINOR_VERSION) -MICRO_VERSION=${MICRO_VERSION:-2} -AC_SUBST(MICRO_VERSION) -EXTRA_VERSION=0 -AC_SUBST(EXTRA_VERSION) - -VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION - -AC_MSG_NOTICE([Configuring LiteBase/TC $MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION-$EXTRA_VERSION]) - -AC_CANONICAL_TARGET() - -AM_INIT_AUTOMAKE($PACKAGE, $VERSION) -AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE") -AC_DEFINE_UNQUOTED(VERSION, "$VERSION") - -# Checks for programs. -#AC_CONFIG_HEADERS([config.h]) -AC_PROG_CC -AC_PROG_OBJC -AM_PROG_LIBTOOL -_AM_DEPENDENCIES([OBJC]) - -#------------------------------------------------------------------------------ -# build targets specifics -#------------------------------------------------------------------------------ -case "$target" in - *-apple-darwin*) - dnl work around since we don't have Objective-C support in autoconf - CFLAGS="-Dlinux -Ddarwin ${CFLAGS}" - OBJCFLAGS="-Dlinux -Ddarwin ${OBJCFLAGS}" - DARWIN_ROOTDIR=`dirname \`which $target-gcc\``/.. - if test "x$target" = "xarm-apple-darwin9"; then - CFLAGS="-Ddarwin9 ${CFLAGS}" - OBJCFLAGS="-Ddarwin9 ${OBJCFLAGS}" - PLATFORM=darwin9 - DARWIN_ROOTDIR="${DARWIN_ROOTDIR}/.." - AC_MSG_RESULT(iphoneOS_2x) - else - PLATFORM=darwin - AC_MSG_RESULT(iphoneOS_1x) - fi - ;; - *-linux*) - PLATFORM=linux - ;; - *) - AC_MSG_ERROR([Unsupported target: Fix the configure.in if it should be]) - ;; -esac -AC_SUBST(PLATFORM) -AC_SUBST(DARWIN_ROOTDIR) - -#------------------------------------------------------------------------------ -# Enable documentation build. -#------------------------------------------------------------------------------ -AC_ARG_ENABLE(doxygen, - AC_HELP_STRING(--enable-doxygen,[enable documentation generation with doxygen])) -enable_doc=no -if test "x$enable_doxygen" = "xyes"; then - AC_PATH_PROG(DOXYGEN, doxygen, , $PATH) - if test "x$DOXYGEN" = "x"; then - AC_MSG_ERROR([could not find doxygen]) - else - enable_doc=yes - fi -fi -AM_CONDITIONAL(DOC, test x$enable_doc = "xyes") - -#------------------------------------------------------------------------------ -# Enable testsuite. -#------------------------------------------------------------------------------ -AC_ARG_ENABLE(testsuite, - AC_HELP_STRING(--enable-testsuite,[enable testsuite running])) -AM_CONDITIONAL(TESTSUITE, test "x$enable_testsuite" = "xyes") - -#------------------------------------------------------------------------------ -# Enable demo version. -#------------------------------------------------------------------------------ -AC_ARG_ENABLE(demo, - AC_HELP_STRING(--enable-demo,[enable demo version])) -AM_CONDITIONAL(DEMO_VERSION, test "x$enable_demo" = "xyes") - -# standard native libs -NATIVE_LIBS= - -AC_SUBST(NATIVE_LIBS) - -DISTRIB_LIBS= -for l in $NATIVE_LIBS -do - DISTRIB_LIBS="\${lib_path}/vm/linux/$l $DISTRIB_LIBS" -done -AC_SUBST(DISTRIB_LIBS) - -#------------------------------------------------------------------------------ -# Release or Debug build. -#------------------------------------------------------------------------------ -AC_MSG_CHECKING([whether to enable release build]) -AC_ARG_ENABLE(release, - [AC_HELP_STRING([--enable-release],[compile with release options])], - USE_RELEASE_FLAGS="${enableval}", - USE_RELEASE_FLAGS="yes" -) -if test "x$GCC" = "xyes"; then - CFLAGS=`echo ${CFLAGS} | sed 's/-g//' | sed 's/-O[[^ ]]*//'`; - if test "x$USE_RELEASE_FLAGS" = "xyes"; then - CFLAGS="-O3 -fno-strict-aliasing -fomit-frame-pointer ${CFLAGS}" - else - CFLAGS="-O0 -g -fno-strict-aliasing ${CFLAGS}" - # to use the native iPhone gdb, these settings are required: see http://www.246tnt.com/iPhone/ - if test "x$PLATFORM" == "xdarwin9"; then - CFLAGS="-ggdb -march=armv6 -mcpu=arm1176jzf-s ${CFLAGS}" - fi - fi -fi -AC_MSG_RESULT($USE_RELEASE_FLAGS) - -# Checks for header files. -AC_HEADER_DIRENT -AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h math.h malloc.h memory.h stdarg.h stddef.h stdlib.h string.h sys/param.h sys/time.h sys/timeb.h sys/statfs.h unistd.h]) -AC_CHECK_HEADERS(getopt.h,AC_DEFINE(HAVE_GETOPT_H,1,[getopt.h is needed for getopt_long support]),AC_MSG_WARN(getopt.h was not found.)) - -# Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_HEADER_STDBOOL -AC_C_INLINE -AC_TYPE_SIZE_T -AC_HEADER_TIME -AC_STRUCT_TM -AC_HEADER_SYS_WAIT -AC_CHECK_TYPES([ptrdiff_t]) - -# Checks for library functions. -AC_FUNC_ALLOCA -AC_FUNC_CLOSEDIR_VOID -AC_FUNC_MKTIME -AC_FUNC_MMAP -AC_FUNC_REALLOC -AC_FUNC_STAT -AC_CHECK_FUNCS([statfs getopt_long getenv ftruncate getpagesize gettimeofday localtime_r memset munmap pow]) - -#------------------------------------------------------------------------------ -# Specify the TotalCrossVM folder -#------------------------------------------------------------------------------ -AC_MSG_CHECKING(TotalCrossVM root folder) -AC_ARG_WITH(tcvm-prefix, - AC_HELP_STRING(--with-tcvm-prefix=folder, [Prefix where TotalCrossVM is installed]), - [ac_with_tcvm_prefix=$withval], - [ac_with_tcvm_prefix=no]) -case "$ac_with_tcvm_prefix" in - no) - AC_MSG_RESULT(no) - TOTALCROSS_TCVM_ROOT=../../TotalCrossVM - ;; - *) - TOTALCROSS_TCVM_ROOT="$ac_with_tcvm_prefix" - AC_DEFINE(TOTALCROSS_TCVM_ROOT, $TOTALCROSS_TCVM_ROOT, TotalCrossVM root folder) - AC_SUBST(TOTALCROSS_TCVM_ROOT) - AC_MSG_RESULT($ac_with_tcvm_prefix) - ;; -esac - -#------------------------------------------------------------------------------ -# Specify the TotalCrossSDK folder -#------------------------------------------------------------------------------ -AC_MSG_CHECKING(TotalCrossSDK root folder) -AC_ARG_WITH(sdk-prefix, - AS_HELP_STRING([--with-sdk-prefix=folder],[Prefix where TotalCrossSDK is installed]), - [ac_with_sdk_prefix=$withval], - [ac_with_sdk_prefix=no]) -case "$ac_with_sdk_prefix" in - no) - AC_MSG_RESULT(no) - TOTALCROSS_SDK_ROOT= - ;; - *) - TOTALCROSS_SDK_ROOT="$ac_with_sdk_prefix" - AC_DEFINE(TOTALCROSS_SDK_ROOT, $TOTALCROSS_SDK_ROOT, TotalCrossSDK root folder) - AC_SUBST(TOTALCROSS_SDK_ROOT) - #AC_CONFIG_LINKS(lib:$TOTALCROSS_SDK_ROOT/lib) - AC_MSG_RESULT($ac_with_sdk_prefix) - ;; -esac - -AM_CONDITIONAL(LINUX, test ${PLATFORM} == linux) -AM_CONDITIONAL(DARWIN, test ${PLATFORM} == darwin || test ${PLATFORM} == darwin9) -AM_CONDITIONAL(DARWIN9, test ${PLATFORM} == darwin9) - -#------------------------------------------------------------------------------ -# Templates processing. -#------------------------------------------------------------------------------ -AC_CONFIG_HEADER(config.h) -#AC_CONFIG_FILES([Makefile litebase.spec docs/Makefile docs/Doxyfile]) -AC_CONFIG_FILES([Makefile]) - -AC_OUTPUT - -AC_MSG_NOTICE([]) -AC_MSG_NOTICE([================== You can proceed with: ====================]) -AC_MSG_NOTICE([- 'make' to build the Litebase library.]) -AC_MSG_NOTICE([- 'make dist' to build a source tarball.]) -AC_MSG_NOTICE([- 'make dist-samples' to build a "source" tarball of the samples.]) -AC_MSG_NOTICE([- 'make rpm' to build redhat source/release RPMs.]) -AC_MSG_NOTICE([- 'make rpm2deb' to convert redhat release RPMs to debian packages (requires alien).]) -AC_MSG_NOTICE([- 'make deploy-all' to build everything.]) -AC_MSG_NOTICE([]) -AC_MSG_NOTICE([*) All targets except the first require the --with-tcvm-prefix option]) -AC_MSG_NOTICE([=============================================================]) -AC_MSG_NOTICE([]) diff --git a/LitebaseSDK/builders/sdk_sfx.diz b/LitebaseSDK/builders/sdk_sfx.diz deleted file mode 100644 index b7e0d413bc..0000000000 --- a/LitebaseSDK/builders/sdk_sfx.diz +++ /dev/null @@ -1,190 +0,0 @@ -Title=Litebase SDK @VERSION@ DEMO -Path=c:\LitebaseSDK -Overwrite=1 -Setup=setup.bat - -Text -{ -Welcome to the installation of Litebase SDK @VERSION@ DEMO. The SDK is fully functional, with all libraries included in the licensed version, -but there's a popup displayed each time LitebaseConnection.getInstance is called. - - -It is always a good idea to expand this into a new folder. To uninstall this software, just delete the target folder; no registry settings are changed. - - -Enjoy! -} - -License -{ -TO INSTALL TOTALCROSS YOU MUST AGREE WITH THIS LICENSE, WHICH APPLIES TO THE SDK BUT NOT TO THE VIRTUAL MACHINES - - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. - - END OF TERMS AND CONDITIONS -} diff --git a/LitebaseSDK/builders/tskill.exe b/LitebaseSDK/builders/tskill.exe deleted file mode 100644 index 2aed9f4fbd..0000000000 Binary files a/LitebaseSDK/builders/tskill.exe and /dev/null differ diff --git a/LitebaseSDK/builders/vc2008/.gitignore b/LitebaseSDK/builders/vc2008/.gitignore deleted file mode 100644 index fbae27973c..0000000000 --- a/LitebaseSDK/builders/vc2008/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/*.vcproj.*.user diff --git a/LitebaseSDK/builders/vc2008/Litebase.vcproj b/LitebaseSDK/builders/vc2008/Litebase.vcproj deleted file mode 100644 index f739243870..0000000000 --- a/LitebaseSDK/builders/vc2008/Litebase.vcproj +++ /dev/null @@ -1,926 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LitebaseSDK/builders/vc2008/Litebase.vcxproj b/LitebaseSDK/builders/vc2008/Litebase.vcxproj deleted file mode 100644 index 56ddd5389e..0000000000 --- a/LitebaseSDK/builders/vc2008/Litebase.vcxproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - Template - Win32 - - - - - - - - - - Application - - - - - - - - - - - - - - - - - - - - - - diff --git a/LitebaseSDK/builders/vms_sfx.diz b/LitebaseSDK/builders/vms_sfx.diz deleted file mode 100644 index d02b608366..0000000000 --- a/LitebaseSDK/builders/vms_sfx.diz +++ /dev/null @@ -1,19 +0,0 @@ -Title=Litebase VMS @VERSION@ -Path=c:\LitebaseVMS -Overwrite=1 - -Text -{ -Welcome to the installation of Litebase VMS @VERSION@ FULL. - - -It is always a good idea to expand this into a new folder. To uninstall this software, just delete the target folder; no registry settings are changed. - - -Enjoy! -} - -License -{ -This software is licenses PER DEVICE. You must use only the purchased number of keys. Do you agree with that? -} diff --git a/LitebaseSDK/builders/xcode/Litebase-Prefix.pch b/LitebaseSDK/builders/xcode/Litebase-Prefix.pch deleted file mode 100644 index 0c7b6b0617..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase-Prefix.pch +++ /dev/null @@ -1,7 +0,0 @@ -// -// Prefix header for all source files of the 'Litebase' target in the 'Litebase' project -// - -#ifdef __OBJC__ - #import -#endif diff --git a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/project.pbxproj b/LitebaseSDK/builders/xcode/Litebase.xcodeproj/project.pbxproj deleted file mode 100644 index 8057ea5c21..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/project.pbxproj +++ /dev/null @@ -1,541 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 0F7CBA061552CC8600E9BD43 /* nativeProcAddressesLB.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F7CBA041552CC8600E9BD43 /* nativeProcAddressesLB.c */; }; - 0F7CBA071552CC8600E9BD43 /* nativeProcAddressesLB.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7CBA051552CC8600E9BD43 /* nativeProcAddressesLB.h */; }; - 0F91CCF0154EDFA8000868DA /* Constants.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCB0154EDFA8000868DA /* Constants.h */; }; - 0F91CCF1154EDFA8000868DA /* Index.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCB1154EDFA8000868DA /* Index.c */; }; - 0F91CCF2154EDFA8000868DA /* Index.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCB2154EDFA8000868DA /* Index.h */; }; - 0F91CCF3154EDFA8000868DA /* Key.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCB3154EDFA8000868DA /* Key.c */; }; - 0F91CCF4154EDFA8000868DA /* Key.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCB4154EDFA8000868DA /* Key.h */; }; - 0F91CCF5154EDFA8000868DA /* lbFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCB5154EDFA8000868DA /* lbFile.c */; }; - 0F91CCF6154EDFA8000868DA /* lbFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCB6154EDFA8000868DA /* lbFile.h */; }; - 0F91CCF7154EDFA8000868DA /* Litebase.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCB7154EDFA8000868DA /* Litebase.c */; }; - 0F91CCF8154EDFA8000868DA /* Litebase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCB8154EDFA8000868DA /* Litebase.h */; }; - 0F91CCF9154EDFA8000868DA /* LitebaseGlobals.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCB9154EDFA8000868DA /* LitebaseGlobals.c */; }; - 0F91CCFA154EDFA8000868DA /* LitebaseGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCBA154EDFA8000868DA /* LitebaseGlobals.h */; }; - 0F91CCFB154EDFA8000868DA /* LitebaseTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCBB154EDFA8000868DA /* LitebaseTypes.h */; }; - 0F91CCFC154EDFA8000868DA /* Macros.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCBC154EDFA8000868DA /* Macros.h */; }; - 0F91CCFD154EDFA8000868DA /* MarkBits.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCBD154EDFA8000868DA /* MarkBits.c */; }; - 0F91CCFE154EDFA8000868DA /* MarkBits.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCBE154EDFA8000868DA /* MarkBits.h */; }; - 0F91CCFF154EDFA8000868DA /* MemoryFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCBF154EDFA8000868DA /* MemoryFile.c */; }; - 0F91CD00154EDFA8000868DA /* MemoryFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCC0154EDFA8000868DA /* MemoryFile.h */; }; - 0F91CD01154EDFA8000868DA /* NativeMethods.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCC1154EDFA8000868DA /* NativeMethods.c */; }; - 0F91CD02154EDFA8000868DA /* NativeMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCC2154EDFA8000868DA /* NativeMethods.h */; }; - 0F91CD03154EDFA8000868DA /* Node.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCC5154EDFA8000868DA /* Node.c */; }; - 0F91CD04154EDFA8000868DA /* Node.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCC6154EDFA8000868DA /* Node.h */; }; - 0F91CD05154EDFA8000868DA /* NormalFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCC7154EDFA8000868DA /* NormalFile.c */; }; - 0F91CD06154EDFA8000868DA /* NormalFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCC8154EDFA8000868DA /* NormalFile.h */; }; - 0F91CD07154EDFA8000868DA /* LitebaseLex.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCCA154EDFA8000868DA /* LitebaseLex.c */; }; - 0F91CD08154EDFA8000868DA /* LitebaseLex.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCCB154EDFA8000868DA /* LitebaseLex.h */; }; - 0F91CD09154EDFA8000868DA /* LitebaseMessage.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCCC154EDFA8000868DA /* LitebaseMessage.c */; }; - 0F91CD0A154EDFA8000868DA /* LitebaseMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCCD154EDFA8000868DA /* LitebaseMessage.h */; }; - 0F91CD0B154EDFA8000868DA /* LitebaseParser.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCCE154EDFA8000868DA /* LitebaseParser.c */; }; - 0F91CD0C154EDFA8000868DA /* LitebaseParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCCF154EDFA8000868DA /* LitebaseParser.h */; }; - 0F91CD0E154EDFA8000868DA /* SQLBooleanClause.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCD1154EDFA8000868DA /* SQLBooleanClause.c */; }; - 0F91CD0F154EDFA8000868DA /* SQLBooleanClause.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCD2154EDFA8000868DA /* SQLBooleanClause.h */; }; - 0F91CD10154EDFA8000868DA /* SQLBooleanClauseTree.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCD3154EDFA8000868DA /* SQLBooleanClauseTree.c */; }; - 0F91CD11154EDFA8000868DA /* SQLBooleanClauseTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCD4154EDFA8000868DA /* SQLBooleanClauseTree.h */; }; - 0F91CD12154EDFA8000868DA /* SQLColumnListClause.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCD5154EDFA8000868DA /* SQLColumnListClause.c */; }; - 0F91CD13154EDFA8000868DA /* SQLColumnListClause.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCD6154EDFA8000868DA /* SQLColumnListClause.h */; }; - 0F91CD14154EDFA8000868DA /* SQLDeleteStatement.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCD7154EDFA8000868DA /* SQLDeleteStatement.c */; }; - 0F91CD15154EDFA8000868DA /* SQLDeleteStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCD8154EDFA8000868DA /* SQLDeleteStatement.h */; }; - 0F91CD16154EDFA8000868DA /* SQLInsertStatement.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCD9154EDFA8000868DA /* SQLInsertStatement.c */; }; - 0F91CD17154EDFA8000868DA /* SQLInsertStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCDA154EDFA8000868DA /* SQLInsertStatement.h */; }; - 0F91CD18154EDFA8000868DA /* SQLSelectStatement.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCDB154EDFA8000868DA /* SQLSelectStatement.c */; }; - 0F91CD19154EDFA8000868DA /* SQLSelectStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCDC154EDFA8000868DA /* SQLSelectStatement.h */; }; - 0F91CD1A154EDFA8000868DA /* SQLUpdateStatement.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCDD154EDFA8000868DA /* SQLUpdateStatement.c */; }; - 0F91CD1B154EDFA8000868DA /* SQLUpdateStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCDE154EDFA8000868DA /* SQLUpdateStatement.h */; }; - 0F91CD1C154EDFA8000868DA /* PlainDB.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCDF154EDFA8000868DA /* PlainDB.c */; }; - 0F91CD1D154EDFA8000868DA /* PlainDB.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCE0154EDFA8000868DA /* PlainDB.h */; }; - 0F91CD1E154EDFA8000868DA /* PreparedStatement.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCE1154EDFA8000868DA /* PreparedStatement.c */; }; - 0F91CD1F154EDFA8000868DA /* PreparedStatement.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCE2154EDFA8000868DA /* PreparedStatement.h */; }; - 0F91CD20154EDFA8000868DA /* ResultSet.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCE3154EDFA8000868DA /* ResultSet.c */; }; - 0F91CD21154EDFA8000868DA /* ResultSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCE4154EDFA8000868DA /* ResultSet.h */; }; - 0F91CD22154EDFA8000868DA /* SQLValue.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCE5154EDFA8000868DA /* SQLValue.c */; }; - 0F91CD23154EDFA8000868DA /* SQLValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCE6154EDFA8000868DA /* SQLValue.h */; }; - 0F91CD24154EDFA8000868DA /* Table.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCE7154EDFA8000868DA /* Table.c */; }; - 0F91CD25154EDFA8000868DA /* Table.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCE8154EDFA8000868DA /* Table.h */; }; - 0F91CD26154EDFA8000868DA /* TCVMLib.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCE9154EDFA8000868DA /* TCVMLib.c */; }; - 0F91CD27154EDFA8000868DA /* TCVMLib.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCEA154EDFA8000868DA /* TCVMLib.h */; }; - 0F91CD28154EDFA8000868DA /* UtilsLB.c in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CCEB154EDFA8000868DA /* UtilsLB.c */; }; - 0F91CD29154EDFA8000868DA /* UtilsLB.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CCEC154EDFA8000868DA /* UtilsLB.h */; }; - 0F91CD2E154EE3D6000868DA /* liblitebase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F91CD2C154EE3D6000868DA /* liblitebase.h */; }; - 0F91CD2F154EE3D6000868DA /* liblitebase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0F91CD2D154EE3D6000868DA /* liblitebase.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 0F7CBA041552CC8600E9BD43 /* nativeProcAddressesLB.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nativeProcAddressesLB.c; sourceTree = ""; }; - 0F7CBA051552CC8600E9BD43 /* nativeProcAddressesLB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nativeProcAddressesLB.h; sourceTree = ""; }; - 0F91CB7B154EDC7A000868DA /* libLitebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libLitebase.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 0F91CB82154EDC7A000868DA /* Litebase-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Litebase-Prefix.pch"; sourceTree = ""; }; - 0F91CCB0154EDFA8000868DA /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; }; - 0F91CCB1154EDFA8000868DA /* Index.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Index.c; sourceTree = ""; }; - 0F91CCB2154EDFA8000868DA /* Index.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Index.h; sourceTree = ""; }; - 0F91CCB3154EDFA8000868DA /* Key.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Key.c; sourceTree = ""; }; - 0F91CCB4154EDFA8000868DA /* Key.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Key.h; sourceTree = ""; }; - 0F91CCB5154EDFA8000868DA /* lbFile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lbFile.c; sourceTree = ""; }; - 0F91CCB6154EDFA8000868DA /* lbFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lbFile.h; sourceTree = ""; }; - 0F91CCB7154EDFA8000868DA /* Litebase.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Litebase.c; sourceTree = ""; }; - 0F91CCB8154EDFA8000868DA /* Litebase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Litebase.h; sourceTree = ""; }; - 0F91CCB9154EDFA8000868DA /* LitebaseGlobals.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LitebaseGlobals.c; sourceTree = ""; }; - 0F91CCBA154EDFA8000868DA /* LitebaseGlobals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LitebaseGlobals.h; sourceTree = ""; }; - 0F91CCBB154EDFA8000868DA /* LitebaseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LitebaseTypes.h; sourceTree = ""; }; - 0F91CCBC154EDFA8000868DA /* Macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Macros.h; sourceTree = ""; }; - 0F91CCBD154EDFA8000868DA /* MarkBits.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MarkBits.c; sourceTree = ""; }; - 0F91CCBE154EDFA8000868DA /* MarkBits.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MarkBits.h; sourceTree = ""; }; - 0F91CCBF154EDFA8000868DA /* MemoryFile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = MemoryFile.c; sourceTree = ""; }; - 0F91CCC0154EDFA8000868DA /* MemoryFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryFile.h; sourceTree = ""; }; - 0F91CCC1154EDFA8000868DA /* NativeMethods.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = NativeMethods.c; sourceTree = ""; }; - 0F91CCC2154EDFA8000868DA /* NativeMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeMethods.h; sourceTree = ""; }; - 0F91CCC3154EDFA8000868DA /* NativeMethods.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NativeMethods.txt; sourceTree = ""; }; - 0F91CCC4154EDFA8000868DA /* NativeMethodsPrototypes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NativeMethodsPrototypes.txt; sourceTree = ""; }; - 0F91CCC5154EDFA8000868DA /* Node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Node.c; sourceTree = ""; }; - 0F91CCC6154EDFA8000868DA /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Node.h; sourceTree = ""; }; - 0F91CCC7154EDFA8000868DA /* NormalFile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = NormalFile.c; sourceTree = ""; }; - 0F91CCC8154EDFA8000868DA /* NormalFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NormalFile.h; sourceTree = ""; }; - 0F91CCCA154EDFA8000868DA /* LitebaseLex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LitebaseLex.c; sourceTree = ""; }; - 0F91CCCB154EDFA8000868DA /* LitebaseLex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LitebaseLex.h; sourceTree = ""; }; - 0F91CCCC154EDFA8000868DA /* LitebaseMessage.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = LitebaseMessage.c; sourceTree = ""; }; - 0F91CCCD154EDFA8000868DA /* LitebaseMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LitebaseMessage.h; sourceTree = ""; }; - 0F91CCCE154EDFA8000868DA /* LitebaseParser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LitebaseParser.c; sourceTree = ""; }; - 0F91CCCF154EDFA8000868DA /* LitebaseParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LitebaseParser.h; sourceTree = ""; }; - 0F91CCD1154EDFA8000868DA /* SQLBooleanClause.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLBooleanClause.c; sourceTree = ""; }; - 0F91CCD2154EDFA8000868DA /* SQLBooleanClause.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLBooleanClause.h; sourceTree = ""; }; - 0F91CCD3154EDFA8000868DA /* SQLBooleanClauseTree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLBooleanClauseTree.c; sourceTree = ""; }; - 0F91CCD4154EDFA8000868DA /* SQLBooleanClauseTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLBooleanClauseTree.h; sourceTree = ""; }; - 0F91CCD5154EDFA8000868DA /* SQLColumnListClause.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLColumnListClause.c; sourceTree = ""; }; - 0F91CCD6154EDFA8000868DA /* SQLColumnListClause.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLColumnListClause.h; sourceTree = ""; }; - 0F91CCD7154EDFA8000868DA /* SQLDeleteStatement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLDeleteStatement.c; sourceTree = ""; }; - 0F91CCD8154EDFA8000868DA /* SQLDeleteStatement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLDeleteStatement.h; sourceTree = ""; }; - 0F91CCD9154EDFA8000868DA /* SQLInsertStatement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLInsertStatement.c; sourceTree = ""; }; - 0F91CCDA154EDFA8000868DA /* SQLInsertStatement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLInsertStatement.h; sourceTree = ""; }; - 0F91CCDB154EDFA8000868DA /* SQLSelectStatement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLSelectStatement.c; sourceTree = ""; }; - 0F91CCDC154EDFA8000868DA /* SQLSelectStatement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLSelectStatement.h; sourceTree = ""; }; - 0F91CCDD154EDFA8000868DA /* SQLUpdateStatement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLUpdateStatement.c; sourceTree = ""; }; - 0F91CCDE154EDFA8000868DA /* SQLUpdateStatement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLUpdateStatement.h; sourceTree = ""; }; - 0F91CCDF154EDFA8000868DA /* PlainDB.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PlainDB.c; sourceTree = ""; }; - 0F91CCE0154EDFA8000868DA /* PlainDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlainDB.h; sourceTree = ""; }; - 0F91CCE1154EDFA8000868DA /* PreparedStatement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PreparedStatement.c; sourceTree = ""; }; - 0F91CCE2154EDFA8000868DA /* PreparedStatement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreparedStatement.h; sourceTree = ""; }; - 0F91CCE3154EDFA8000868DA /* ResultSet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ResultSet.c; sourceTree = ""; }; - 0F91CCE4154EDFA8000868DA /* ResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResultSet.h; sourceTree = ""; }; - 0F91CCE5154EDFA8000868DA /* SQLValue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SQLValue.c; sourceTree = ""; }; - 0F91CCE6154EDFA8000868DA /* SQLValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLValue.h; sourceTree = ""; }; - 0F91CCE7154EDFA8000868DA /* Table.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Table.c; sourceTree = ""; }; - 0F91CCE8154EDFA8000868DA /* Table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Table.h; sourceTree = ""; }; - 0F91CCE9154EDFA8000868DA /* TCVMLib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TCVMLib.c; sourceTree = ""; }; - 0F91CCEA154EDFA8000868DA /* TCVMLib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCVMLib.h; sourceTree = ""; }; - 0F91CCEB154EDFA8000868DA /* UtilsLB.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = UtilsLB.c; sourceTree = ""; }; - 0F91CCEC154EDFA8000868DA /* UtilsLB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UtilsLB.h; sourceTree = ""; }; - 0F91CD2C154EE3D6000868DA /* liblitebase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = liblitebase.h; sourceTree = ""; }; - 0F91CD2D154EE3D6000868DA /* liblitebase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = liblitebase.m; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0F91CB78154EDC7A000868DA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0F91CB70154EDC7A000868DA = { - isa = PBXGroup; - children = ( - 0F91CD2C154EE3D6000868DA /* liblitebase.h */, - 0F91CD2D154EE3D6000868DA /* liblitebase.m */, - 0F91CCAD154EDFA8000868DA /* native */, - 0F91CB80154EDC7A000868DA /* Litebase */, - 0F91CB7D154EDC7A000868DA /* Frameworks */, - 0F91CB7C154EDC7A000868DA /* Products */, - ); - sourceTree = ""; - }; - 0F91CB7C154EDC7A000868DA /* Products */ = { - isa = PBXGroup; - children = ( - 0F91CB7B154EDC7A000868DA /* libLitebase.a */, - ); - name = Products; - sourceTree = ""; - }; - 0F91CB7D154EDC7A000868DA /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; - 0F91CB80154EDC7A000868DA /* Litebase */ = { - isa = PBXGroup; - children = ( - 0F91CB81154EDC7A000868DA /* Supporting Files */, - ); - path = Litebase; - sourceTree = ""; - }; - 0F91CB81154EDC7A000868DA /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 0F91CB82154EDC7A000868DA /* Litebase-Prefix.pch */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 0F91CCAD154EDFA8000868DA /* native */ = { - isa = PBXGroup; - children = ( - 0F7CBA041552CC8600E9BD43 /* nativeProcAddressesLB.c */, - 0F7CBA051552CC8600E9BD43 /* nativeProcAddressesLB.h */, - 0F91CCB0154EDFA8000868DA /* Constants.h */, - 0F91CCB1154EDFA8000868DA /* Index.c */, - 0F91CCB2154EDFA8000868DA /* Index.h */, - 0F91CCB3154EDFA8000868DA /* Key.c */, - 0F91CCB4154EDFA8000868DA /* Key.h */, - 0F91CCB5154EDFA8000868DA /* lbFile.c */, - 0F91CCB6154EDFA8000868DA /* lbFile.h */, - 0F91CCB7154EDFA8000868DA /* Litebase.c */, - 0F91CCB8154EDFA8000868DA /* Litebase.h */, - 0F91CCB9154EDFA8000868DA /* LitebaseGlobals.c */, - 0F91CCBA154EDFA8000868DA /* LitebaseGlobals.h */, - 0F91CCBB154EDFA8000868DA /* LitebaseTypes.h */, - 0F91CCBC154EDFA8000868DA /* Macros.h */, - 0F91CCBD154EDFA8000868DA /* MarkBits.c */, - 0F91CCBE154EDFA8000868DA /* MarkBits.h */, - 0F91CCBF154EDFA8000868DA /* MemoryFile.c */, - 0F91CCC0154EDFA8000868DA /* MemoryFile.h */, - 0F91CCC1154EDFA8000868DA /* NativeMethods.c */, - 0F91CCC2154EDFA8000868DA /* NativeMethods.h */, - 0F91CCC3154EDFA8000868DA /* NativeMethods.txt */, - 0F91CCC4154EDFA8000868DA /* NativeMethodsPrototypes.txt */, - 0F91CCC5154EDFA8000868DA /* Node.c */, - 0F91CCC6154EDFA8000868DA /* Node.h */, - 0F91CCC7154EDFA8000868DA /* NormalFile.c */, - 0F91CCC8154EDFA8000868DA /* NormalFile.h */, - 0F91CCC9154EDFA8000868DA /* parser */, - 0F91CCDF154EDFA8000868DA /* PlainDB.c */, - 0F91CCE0154EDFA8000868DA /* PlainDB.h */, - 0F91CCE1154EDFA8000868DA /* PreparedStatement.c */, - 0F91CCE2154EDFA8000868DA /* PreparedStatement.h */, - 0F91CCE3154EDFA8000868DA /* ResultSet.c */, - 0F91CCE4154EDFA8000868DA /* ResultSet.h */, - 0F91CCE5154EDFA8000868DA /* SQLValue.c */, - 0F91CCE6154EDFA8000868DA /* SQLValue.h */, - 0F91CCE7154EDFA8000868DA /* Table.c */, - 0F91CCE8154EDFA8000868DA /* Table.h */, - 0F91CCE9154EDFA8000868DA /* TCVMLib.c */, - 0F91CCEA154EDFA8000868DA /* TCVMLib.h */, - 0F91CCEB154EDFA8000868DA /* UtilsLB.c */, - 0F91CCEC154EDFA8000868DA /* UtilsLB.h */, - ); - name = native; - path = ../../src/native; - sourceTree = ""; - }; - 0F91CCC9154EDFA8000868DA /* parser */ = { - isa = PBXGroup; - children = ( - 0F91CCCA154EDFA8000868DA /* LitebaseLex.c */, - 0F91CCCB154EDFA8000868DA /* LitebaseLex.h */, - 0F91CCCC154EDFA8000868DA /* LitebaseMessage.c */, - 0F91CCCD154EDFA8000868DA /* LitebaseMessage.h */, - 0F91CCCE154EDFA8000868DA /* LitebaseParser.c */, - 0F91CCCF154EDFA8000868DA /* LitebaseParser.h */, - 0F91CCD1154EDFA8000868DA /* SQLBooleanClause.c */, - 0F91CCD2154EDFA8000868DA /* SQLBooleanClause.h */, - 0F91CCD3154EDFA8000868DA /* SQLBooleanClauseTree.c */, - 0F91CCD4154EDFA8000868DA /* SQLBooleanClauseTree.h */, - 0F91CCD5154EDFA8000868DA /* SQLColumnListClause.c */, - 0F91CCD6154EDFA8000868DA /* SQLColumnListClause.h */, - 0F91CCD7154EDFA8000868DA /* SQLDeleteStatement.c */, - 0F91CCD8154EDFA8000868DA /* SQLDeleteStatement.h */, - 0F91CCD9154EDFA8000868DA /* SQLInsertStatement.c */, - 0F91CCDA154EDFA8000868DA /* SQLInsertStatement.h */, - 0F91CCDB154EDFA8000868DA /* SQLSelectStatement.c */, - 0F91CCDC154EDFA8000868DA /* SQLSelectStatement.h */, - 0F91CCDD154EDFA8000868DA /* SQLUpdateStatement.c */, - 0F91CCDE154EDFA8000868DA /* SQLUpdateStatement.h */, - ); - path = parser; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 0F91CB79154EDC7A000868DA /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 0F91CCF0154EDFA8000868DA /* Constants.h in Headers */, - 0F91CCF2154EDFA8000868DA /* Index.h in Headers */, - 0F91CCF4154EDFA8000868DA /* Key.h in Headers */, - 0F91CCF6154EDFA8000868DA /* lbFile.h in Headers */, - 0F91CCF8154EDFA8000868DA /* Litebase.h in Headers */, - 0F91CCFA154EDFA8000868DA /* LitebaseGlobals.h in Headers */, - 0F91CCFB154EDFA8000868DA /* LitebaseTypes.h in Headers */, - 0F91CCFC154EDFA8000868DA /* Macros.h in Headers */, - 0F91CCFE154EDFA8000868DA /* MarkBits.h in Headers */, - 0F91CD00154EDFA8000868DA /* MemoryFile.h in Headers */, - 0F91CD02154EDFA8000868DA /* NativeMethods.h in Headers */, - 0F91CD04154EDFA8000868DA /* Node.h in Headers */, - 0F91CD06154EDFA8000868DA /* NormalFile.h in Headers */, - 0F91CD08154EDFA8000868DA /* LitebaseLex.h in Headers */, - 0F91CD0A154EDFA8000868DA /* LitebaseMessage.h in Headers */, - 0F91CD0C154EDFA8000868DA /* LitebaseParser.h in Headers */, - 0F91CD0F154EDFA8000868DA /* SQLBooleanClause.h in Headers */, - 0F91CD11154EDFA8000868DA /* SQLBooleanClauseTree.h in Headers */, - 0F91CD13154EDFA8000868DA /* SQLColumnListClause.h in Headers */, - 0F91CD15154EDFA8000868DA /* SQLDeleteStatement.h in Headers */, - 0F91CD17154EDFA8000868DA /* SQLInsertStatement.h in Headers */, - 0F91CD19154EDFA8000868DA /* SQLSelectStatement.h in Headers */, - 0F91CD1B154EDFA8000868DA /* SQLUpdateStatement.h in Headers */, - 0F91CD1D154EDFA8000868DA /* PlainDB.h in Headers */, - 0F91CD1F154EDFA8000868DA /* PreparedStatement.h in Headers */, - 0F91CD21154EDFA8000868DA /* ResultSet.h in Headers */, - 0F91CD23154EDFA8000868DA /* SQLValue.h in Headers */, - 0F91CD25154EDFA8000868DA /* Table.h in Headers */, - 0F91CD27154EDFA8000868DA /* TCVMLib.h in Headers */, - 0F91CD29154EDFA8000868DA /* UtilsLB.h in Headers */, - 0F91CD2E154EE3D6000868DA /* liblitebase.h in Headers */, - 0F7CBA071552CC8600E9BD43 /* nativeProcAddressesLB.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 0F91CB7A154EDC7A000868DA /* Litebase */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0F91CB9F154EDC7B000868DA /* Build configuration list for PBXNativeTarget "Litebase" */; - buildPhases = ( - 0F91CB77154EDC7A000868DA /* Sources */, - 0F91CB78154EDC7A000868DA /* Frameworks */, - 0F91CB79154EDC7A000868DA /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Litebase; - productName = Litebase; - productReference = 0F91CB7B154EDC7A000868DA /* libLitebase.a */; - productType = "com.apple.product-type.library.static"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 0F91CB72154EDC7A000868DA /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0430; - ORGANIZATIONNAME = "SuperWaba Ltda"; - }; - buildConfigurationList = 0F91CB75154EDC7A000868DA /* Build configuration list for PBXProject "Litebase" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - ); - mainGroup = 0F91CB70154EDC7A000868DA; - productRefGroup = 0F91CB7C154EDC7A000868DA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0F91CB7A154EDC7A000868DA /* Litebase */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXSourcesBuildPhase section */ - 0F91CB77154EDC7A000868DA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0F91CCF1154EDFA8000868DA /* Index.c in Sources */, - 0F91CCF3154EDFA8000868DA /* Key.c in Sources */, - 0F91CCF5154EDFA8000868DA /* lbFile.c in Sources */, - 0F91CCF7154EDFA8000868DA /* Litebase.c in Sources */, - 0F91CCF9154EDFA8000868DA /* LitebaseGlobals.c in Sources */, - 0F91CCFD154EDFA8000868DA /* MarkBits.c in Sources */, - 0F91CCFF154EDFA8000868DA /* MemoryFile.c in Sources */, - 0F91CD01154EDFA8000868DA /* NativeMethods.c in Sources */, - 0F91CD03154EDFA8000868DA /* Node.c in Sources */, - 0F91CD05154EDFA8000868DA /* NormalFile.c in Sources */, - 0F91CD07154EDFA8000868DA /* LitebaseLex.c in Sources */, - 0F91CD09154EDFA8000868DA /* LitebaseMessage.c in Sources */, - 0F91CD0B154EDFA8000868DA /* LitebaseParser.c in Sources */, - 0F91CD0E154EDFA8000868DA /* SQLBooleanClause.c in Sources */, - 0F91CD10154EDFA8000868DA /* SQLBooleanClauseTree.c in Sources */, - 0F91CD12154EDFA8000868DA /* SQLColumnListClause.c in Sources */, - 0F91CD14154EDFA8000868DA /* SQLDeleteStatement.c in Sources */, - 0F91CD16154EDFA8000868DA /* SQLInsertStatement.c in Sources */, - 0F91CD18154EDFA8000868DA /* SQLSelectStatement.c in Sources */, - 0F91CD1A154EDFA8000868DA /* SQLUpdateStatement.c in Sources */, - 0F91CD1C154EDFA8000868DA /* PlainDB.c in Sources */, - 0F91CD1E154EDFA8000868DA /* PreparedStatement.c in Sources */, - 0F91CD20154EDFA8000868DA /* ResultSet.c in Sources */, - 0F91CD22154EDFA8000868DA /* SQLValue.c in Sources */, - 0F91CD24154EDFA8000868DA /* Table.c in Sources */, - 0F91CD26154EDFA8000868DA /* TCVMLib.c in Sources */, - 0F91CD28154EDFA8000868DA /* UtilsLB.c in Sources */, - 0F91CD2F154EE3D6000868DA /* liblitebase.m in Sources */, - 0F7CBA061552CC8600E9BD43 /* nativeProcAddressesLB.c in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 0F91CB9D154EDC7B000868DA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; - CLANG_ENABLE_OBJC_ARC = NO; - COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - POSIX, - linux, - darwin, - FORCE_LIBC_ALLOC, - LB_EXPORTS, - ENABLE_DEMO, - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(PROJECT_DIR)/../../../TotalCrossVM/src", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/litebase", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/tcvm", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/util", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/nm/io", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/nm/lang", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_DYLIB_INSTALL_NAME = liblitebase.dylib; - MACH_O_TYPE = staticlib; - OTHER_LDFLAGS = "-ObjC"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../../TotalCrossVM/src/tcvm/tcvm.h"; - }; - name = Debug; - }; - 0F91CB9E154EDC7B000868DA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; - CLANG_ENABLE_OBJC_ARC = NO; - COPY_PHASE_STRIP = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_PREPROCESSOR_DEFINITIONS = ( - POSIX, - linux, - darwin, - FORCE_LIBC_ALLOC, - LB_EXPORTS, - ); - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(PROJECT_DIR)/../../../TotalCrossVM/src", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/litebase", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/tcvm", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/util", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/nm/io", - "$(PROJECT_DIR)/../../../TotalCrossVM/src/nm/lang", - ); - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - LD_DYLIB_INSTALL_NAME = liblitebase.dylib; - MACH_O_TYPE = staticlib; - OTHER_LDFLAGS = "-ObjC"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - USER_HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../../../TotalCrossVM/src/tcvm/tcvm.h"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 0F91CBA0154EDC7B000868DA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - DSTROOT = /tmp/Litebase.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Litebase-Prefix.pch"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - POSIX, - linux, - darwin, - FORCE_LIBC_ALLOC, - LB_EXPORTS, - ENABLE_DEMO, - ); - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PUBLIC_HEADERS_FOLDER_PATH = ""; - SKIP_INSTALL = YES; - USER_HEADER_SEARCH_PATHS = ""; - }; - name = Debug; - }; - 0F91CBA1154EDC7B000868DA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - DSTROOT = /tmp/Litebase.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "Litebase-Prefix.pch"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PUBLIC_HEADERS_FOLDER_PATH = ""; - SKIP_INSTALL = YES; - USER_HEADER_SEARCH_PATHS = ""; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0F91CB75154EDC7A000868DA /* Build configuration list for PBXProject "Litebase" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0F91CB9D154EDC7B000868DA /* Debug */, - 0F91CB9E154EDC7B000868DA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0F91CB9F154EDC7B000868DA /* Build configuration list for PBXNativeTarget "Litebase" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0F91CBA0154EDC7B000868DA /* Debug */, - 0F91CBA1154EDC7B000868DA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 0F91CB72154EDC7A000868DA /* Project object */; -} diff --git a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/LitebaseSDK/builders/xcode/Litebase.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 33397d6540..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcshareddata/xcschemes/Litebase.xcscheme b/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcshareddata/xcschemes/Litebase.xcscheme deleted file mode 100644 index 83c96e2ab5..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcshareddata/xcschemes/Litebase.xcscheme +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/guilhermehazan.xcuserdatad/xcschemes/xcschememanagement.plist b/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/guilhermehazan.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 6fc6474bf4..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/guilhermehazan.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - SchemeUserState - - Litebase.xcscheme_^#shared#^_ - - orderHint - 0 - - - SuppressBuildableAutocreation - - 0F91CB7A154EDC7A000868DA - - primary - - - 0F91CB8A154EDC7B000868DA - - primary - - - - - diff --git a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/italo.xcuserdatad/xcschemes/xcschememanagement.plist b/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/italo.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 209736a079..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/italo.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SchemeUserState - - Litebase.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/tcbuilder.xcuserdatad/xcschemes/xcschememanagement.plist b/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/tcbuilder.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index e2e025181e..0000000000 --- a/LitebaseSDK/builders/xcode/Litebase.xcodeproj/xcuserdata/tcbuilder.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SuppressBuildableAutocreation - - 0F91CB7A154EDC7A000868DA - - primary - - - - - diff --git a/LitebaseSDK/builders/xcode/liblitebase.h b/LitebaseSDK/builders/xcode/liblitebase.h deleted file mode 100644 index 8c8d9edb5f..0000000000 --- a/LitebaseSDK/builders/xcode/liblitebase.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -#import - -@interface Litebase : NSObject - -+ (void) fillNativeProcAddressesLB; - -@end diff --git a/LitebaseSDK/builders/xcode/liblitebase.m b/LitebaseSDK/builders/xcode/liblitebase.m deleted file mode 100644 index a6a2b747bf..0000000000 --- a/LitebaseSDK/builders/xcode/liblitebase.m +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -#import "tcvm.h" -#import "NativeMethods.h" -#import "liblitebase.h" -#import "nativeProcAddressesLB.h" - -@implementation Litebase - -+ (void) fillNativeProcAddressesLB -{ - fillNativeProcAddressesLB(); -} - -@end diff --git a/LitebaseSDK/docs/Litebase Companion.lyx b/LitebaseSDK/docs/Litebase Companion.lyx deleted file mode 100644 index 22d84dcbab..0000000000 --- a/LitebaseSDK/docs/Litebase Companion.lyx +++ /dev/null @@ -1,22661 +0,0 @@ -#LyX 2.0 created this file. For more info see http://www.lyx.org/ -\lyxformat 413 -\begin_document -\begin_header -\textclass memoir -\begin_preamble -%used by the cover -\usepackage{graphicx} - -%used to insert tab stops in itemize or enumerate -\usepackage{tabto} - -\usepackage[utf8]{inputenc} -\DeclareUnicodeCharacter{2190}{\ifmmode \leftarrow \else \textleftarrow \fi} % override - -% T1 Helvetica scaled -\usepackage[T1]{fontenc} -\usepackage[scaled]{helvet} -\renewcommand*{\familydefault}{\sfdefault} - -% Chapter style -\usepackage{xcolor,calc} - -\makechapterstyle{combined}{ - \setlength{\beforechapskip}{30pt} - \setlength{\midchapskip}{-60pt} - \setlength{\afterchapskip}{60pt} - \renewcommand*{\printchaptername}{} - \renewcommand*{\chapnumfont}{\normalfont\sffamily\bfseries\fontsize{80}{0}\selectfont} - \renewcommand*{\printchapternum}{\flushright\chapnumfont\textcolor[rgb]{.64,.79,.87}{\thechapter}} - \renewcommand*{\chaptitlefont}{\normalfont\sffamily\Huge\bfseries} - \renewcommand*{\printchaptertitle}[1]{% - \raggedright\chaptitlefont\parbox[t]{\textwidth-3cm}{\raggedright##1}} -} - -\chapterstyle{combined} - -% Code listing -\usepackage{listings,color} - -\definecolor{dkgreen}{RGB}{63,127,95} -\definecolor{gray}{rgb}{0.5,0.5,0.5} -\definecolor{light-gray}{rgb}{0.925,0.925,0.925} -\definecolor{mauve}{RGB}{127,0,85} - - \lstset{ - language=Java, - backgroundcolor=\color{light-gray}, - basicstyle=\footnotesize\ttfamily, % Standardschrift - numbers=left, % Ort der Zeilennummern - numberstyle=\tiny, % Stil der Zeilennummern - %stepnumber=2, % Abstand zwischen den Zeilennummern - numbersep=5pt, % Abstand der Nummern zum Text - tabsize=2, % Groesse von Tabs - extendedchars=true, % - breaklines=true, % Zeilen werden Umgebrochen - emph={LEFT,RIGHT,CENTER,TOP,BOTTOM,FILL,FIT,CENTER_OF,BOTTOM_OF,AFTER,BEFORE}, - emphstyle=\color{blue}\textit, - keywordstyle=\color{mauve}\textbf, - frame=b, - % keywordstyle=[2]\textbf, % Stil der Keywords - % keywordstyle=[2]\textbf, % - % keywordstyle=[3]\textbf, % - % keywordstyle=[4]\textbf, \sqrt{\sqrt{}} % - commentstyle=\color{dkgreen}, % comment style - stringstyle=\color{blue}, % string literal style - % stringstyle=\color{white}\ttfamily, % Farbe der String - showspaces=false, % Leerzeichen anzeigen ? - showtabs=false, % Tabs anzeigen ? - xleftmargin=17pt, - framexleftmargin=17pt, - framexrightmargin=5pt, - framexbottommargin=4pt, - %backgroundcolor=\color{lightgray}, - showstringspaces=false % Leerzeichen in Strings anzeigen ? - } - \lstloadlanguages{% Check Dokumentation for further languages ... - %[Visual]Basic - %Pascal - %C - %C++ - %XML - %HTML - Java - } - %\DeclareCaptionFont{blue}{\color{blue}} - - %\captionsetup[lstlisting]{singlelinecheck=false, labelfont={blue}, textfont={blue}} - \usepackage{caption} -\DeclareCaptionFont{white}{\color{white}} -\DeclareCaptionFormat{listing}{\colorbox[cmyk]{0.43, 0.35, 0.35,0.01}{\parbox{\textwidth}{\hspace{15pt}#1#2#3}}} -\captionsetup[lstlisting]{format=listing,labelfont=white,textfont=white, singlelinecheck=false, margin=0pt, font={bf,footnotesize}} - -% New style chapter star, so we can have unumbered chapters to show in ToC -\newcommand{\tocchap}[1]{\addcontentsline{toc}{chapter}{\protect\numberline -{}#1}\markboth{\textsc{#1}}{\textsc{#1}}\thispagestyle{plain}} -\newcommand{\chapterstar}[1]{\chapter*{#1}\tocchap{#1}} - -% Sections are not numbered -\setcounter{secnumdepth}{0} - -% Vertical gap between paragraphs -\setlength{\parskip}{\medskipamount} -\end_preamble -\use_default_options true -\maintain_unincluded_children false -\begin_local_layout -Style Chapterstar -Copystyle Chapter -LatexName chapterstar -LabelType Top_Environment -LabelString "Unnumbered chapter" -LabelFont -Series Bold -Size LARGE -EndFont -End -\end_local_layout -\language english -\language_package default -\inputencoding default -\fontencoding global -\font_roman lmodern -\font_sans default -\font_typewriter courier -\font_default_family default -\use_non_tex_fonts false -\font_sc false -\font_osf false -\font_sf_scale 100 -\font_tt_scale 100 - -\graphics default -\default_output_format default -\output_sync 0 -\bibtex_command default -\index_command default -\paperfontsize 11 -\spacing single -\use_hyperref true -\pdf_title "Litebase Companion" -\pdf_author "SuperWaba" -\pdf_bookmarks true -\pdf_bookmarksnumbered false -\pdf_bookmarksopen false -\pdf_bookmarksopenlevel 1 -\pdf_breaklinks false -\pdf_pdfborder true -\pdf_colorlinks true -\pdf_backref section -\pdf_pdfusetitle true -\pdf_quoted_options "linkcolor=black" -\papersize a4paper -\use_geometry true -\use_amsmath 1 -\use_esint 1 -\use_mhchem 1 -\use_mathdots 1 -\cite_engine basic -\use_bibtopic false -\use_indices false -\paperorientation portrait -\suppress_date true -\use_refstyle 1 -\index Index -\shortcut idx -\color #008000 -\end_index -\leftmargin 2.5cm -\topmargin 2.5cm -\rightmargin 2.5cm -\bottommargin 4cm -\footskip 1cm -\secnumdepth 0 -\tocdepth 3 -\paragraph_separation indent -\paragraph_indentation default -\quotes_language swedish -\papercolumns 1 -\papersides 2 -\paperpagestyle default -\tracking_changes false -\output_changes false -\html_math_output 0 -\html_css_as_file 0 -\html_be_strict false -\end_header - -\begin_body - -\begin_layout Standard -\begin_inset ERT -status open - -\begin_layout Plain Layout - - -\backslash -clearpage -\end_layout - -\begin_layout Plain Layout - -%% temporary titles -\end_layout - -\begin_layout Plain Layout - -% command to provide stretchy vertical space in proportion -\end_layout - -\begin_layout Plain Layout - - -\backslash -newcommand -\backslash -nbvspace[1][3]{ -\backslash -vspace*{ -\backslash -stretch{#1}}} -\end_layout - -\begin_layout Plain Layout - -% allow some slack to avoid under/overfull boxes -\end_layout - -\begin_layout Plain Layout - - -\backslash -newcommand -\backslash -nbstretchyspace{ -\backslash -spaceskip0.5em plus 0.25em minus 0.25em} -\end_layout - -\begin_layout Plain Layout - -% To improve spacing on titlepages -\end_layout - -\begin_layout Plain Layout - - -\backslash -newcommand{ -\backslash -nbtitlestretch}{ -\backslash -spaceskip0.6em} -\end_layout - -\begin_layout Plain Layout - - -\backslash -pagestyle{empty} -\end_layout - -\begin_layout Plain Layout - - -\backslash -begin{center} -\end_layout - -\begin_layout Plain Layout - - -\backslash -bfseries -\end_layout - -\begin_layout Plain Layout - - -\backslash -nbvspace[1] -\end_layout - -\begin_layout Plain Layout - - -\backslash -Huge -\end_layout - -\begin_layout Plain Layout - -{ -\backslash -nbtitlestretch -\backslash -huge -\backslash -textcolor[RGB]{25,60,240} -\end_layout - -\begin_layout Plain Layout - -{The Litebase Companion}} -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - -\backslash -nbvspace[2] -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - -\backslash -includegraphics[scale=1]{images/title} -\end_layout - -\begin_layout Plain Layout - - -\backslash -nbvspace[5] -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - -\backslash -large Version 2.8 -\end_layout - -\begin_layout Plain Layout - - -\backslash -nbvspace[1] -\end_layout - -\begin_layout Plain Layout - - -\backslash -end{center} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -\begin_inset Newpage newpage -\end_inset - - -\begin_inset ERT -status open - -\begin_layout Plain Layout - - -\backslash -pagestyle{companion} -\end_layout - -\end_inset - - -\begin_inset CommandInset toc -LatexCommand tableofcontents - -\end_inset - - -\begin_inset Newpage cleardoublepage -\end_inset - - -\end_layout - -\begin_layout Part -LITEBASE -\end_layout - -\begin_layout Chapter -Litebase -\end_layout - -\begin_layout Section -Overview -\end_layout - -\begin_layout Standard -Litebase is a full-featured database for TotalCross. - As most databases, it uses the SQL language to let you manipulate data. -\end_layout - -\begin_layout Standard -The normal way to access files in TotalCross is through the -\family typewriter -File -\family default -, -\family typewriter -PDBFile -\family default -, -\family typewriter -ResizeStream, -\family default - and -\family typewriter -DataStream -\family default - classes. - When using Litebase, the programmer will have to forget these classes and - use only the ones provided by the -\family typewriter -litebase.* -\family default - packages. - Trying to use the one or more of these classes in conjunction with the - Litebase ones within the same file can lead to data corruption and is not - recommended. - Moreover, some platforms do not let the same file be used concurrently. -\end_layout - -\begin_layout Standard -It is possible to run Litebase on desktop, either as a Java application, - a Linux application, as a windows XP (and up) application, or on all the - supported TotalCross platforms. - So, its easy to create table files on the desktop that can be later synchronize -d with the device. - The files created are interchangeable on all supported platforms. -\end_layout - -\begin_layout Standard -The packages that belong to this library, which are inside the Litebase - files, are specified below. -\end_layout - -\begin_layout Itemize - -\family typewriter -litebase -\family default -: contains the database classes that can be used to manipulate the plain - files via SQL. -\end_layout - -\begin_layout Itemize - -\family typewriter -litebase.ui -\family default -: contains some useful user interface classes that can be used with the - driver. -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -DBListBox -\family default -: a class used to show a single list box of a -\family typewriter -ResultSet -\family default - that returns multiple columns. - Because it extends -\family typewriter -totalcross.ui.ListBox -\family default -, it can be placed in a combo box too. -\end_layout - -\end_deeper -\begin_layout Standard -These samples are provided under samples: -\end_layout - -\begin_layout Itemize - -\family typewriter -addressbook -\family default -: a simple address book application, which demonstrates the use of the grid - and blobs to insert pictures. -\end_layout - -\begin_layout Itemize - -\family typewriter -bench -\family default -: checks the search and insert speed of Litebase. - Note that it can take some minutes to run on a device. -\end_layout - -\begin_layout Itemize - -\family typewriter -testcases -\family default -: -\family typewriter -TestUnit -\family default - test cases for Litebase: it will give you an overall idea of what is supported. - This can take some minutes to run on a device! -\end_layout - -\begin_layout Itemize - -\family typewriter -sqlconsole -\family default -: a console for issuing SQL commands. - In this application, it is impossible to use prepared statements and blobs. - Using this program you can view the results of queries and create, drop, - and change tables. -\end_layout - -\begin_layout Itemize - -\family typewriter -salesplus -\family default -: a simple sales force application. -\end_layout - -\begin_layout Itemize - -\family typewriter -logger -\family default -: a program for executing a log file and try to reproduce a problem. -\end_layout - -\begin_layout Standard -Next a sample program that uses Litebase is provided: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// Creates the driver's instance. -\end_layout - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance("Test"); -\end_layout - -\begin_layout Plain Layout - -// Creates the table and the index. -\end_layout - -\begin_layout Plain Layout - -try -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - driver.execute("CREATE TABLE person (name char(30), sal double, age int)"); -\end_layout - -\begin_layout Plain Layout - - driver.execute("CREATE INDEX idx_name ON person(name)"); -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -catch (AlreadyCreatedException exception) {} -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Inserts some Values. -\end_layout - -\begin_layout Plain Layout - -driver.executeUpdate("INSERT INTO person VALUES ('Michelle', 3000.0, 31)"); -\end_layout - -\begin_layout Plain Layout - -driver.executeUpdate("INSERT INTO person VALUES ('Simone', 3000.0, 33)"); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Does a query. -\end_layout - -\begin_layout Plain Layout - -ResultSet rs = driver.executeQuery("SELECT * FROM person WHERE name = 'Michelle'" -); -\end_layout - -\begin_layout Plain Layout - -if (rs.next()) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - String name = rs.getString("name"); -\end_layout - -\begin_layout Plain Layout - - double sal = rs.getDouble(2); -\end_layout - -\begin_layout Plain Layout - - ... -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Section -Table Format -\end_layout - -\begin_layout Standard -All database data is written directly to the non-volatile memory (AKA flash-memo -ry). - In the past, each database table file could have at most four kinds of - files: -\family typewriter -.db -\family default - (table header and data), -\family typewriter -.dbo -\family default - (database objects), -\family typewriter -.idk -\family default - (index keys), and -\family typewriter -.idr -\family default - (index repetitions). - There would be an -\family typewriter -.idk -\family default - file for each index and an -\family typewriter -.idr -\family default - file if the index field has repeated data. - Otherwise, the -\family typewriter -.idr -\family default - file would be missing. - On the current versions, the -\family typewriter -.idr -\family default - files do not exist anymore. - It makes index searches faster. - On the other hand, if there are too many key repetitions, an index that - was possible to be created in previous versions may not be able to be created - with the current versions because the number of maximum index keys may - exceed even though the maximum number of keys have been duplicated. - The string fields of a table are stored in a separate file ( -\family typewriter -.dbo -\family default -). - This was necessary to save space from the records, since strings may use - much less space than the one declared when creating a table and all the - records have fixed-size length. - A similar approach is used for blob fields: they are stored in the same - file. -\end_layout - -\begin_layout Standard -The index file format is a specialized B-Tree. - Due to this, the whole table's column is stored in the tree keys (in other - words, in the file). - A big increase in the index size of columns of the string types does not - happen because in this case the dbo position is saved in the key instead - of the data. -\end_layout - -\begin_layout Standard -Whenever the index format is changed, to make use of these and use the correct - format, it is necessary to recreate all the indices. - One way of doing this is just droping the index files manually that the - Litebase will recover them automatically. - Another way of doing it is erasing them in the application. - A simple code fragment is provided below that shows how to drop all the - indices of an application whose tables are stored in -\family typewriter -/Litebase_DBs/ -\family default - and application id is -\family typewriter -ACTv -\family default -: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// ...... -\end_layout - -\begin_layout Plain Layout - -import totalcross.sys.*; -\end_layout - -\begin_layout Plain Layout - -import totalcross.io.*; -\end_layout - -\begin_layout Plain Layout - -// ...... - -\end_layout - -\begin_layout Plain Layout - -try -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - // The folder where the tables files are stored. -\end_layout - -\begin_layout Plain Layout - - String sourcePath = "/Litebase_DBs/", -\end_layout - -\begin_layout Plain Layout - - // The application id of your database. -\end_layout - -\begin_layout Plain Layout - - appCreatorId = "ACTv"; -\end_layout - -\begin_layout Plain Layout - - // Lists all the files of the given folder. -\end_layout - -\begin_layout Plain Layout - - String[] files = new File(sourcePath, File.DONT_OPEN, 1).listFiles(); -\end_layout - -\begin_layout Plain Layout - - Vm.debug("source path: " + sourcePath); -\end_layout - -\begin_layout Plain Layout - - if (files != null) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - int size = files.length; -\end_layout - -\begin_layout Plain Layout - - String name; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - Vm.debug("size: " + size); -\end_layout - -\begin_layout Plain Layout - - while (--size >= 0) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - name = files[size]; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // Files of this application. - -\end_layout - -\begin_layout Plain Layout - - if (name.startsWith(appCreatorId + '-' ) && (name.endsWith(".idk") - -\end_layout - -\begin_layout Plain Layout - - || name.endsWith(".idr"))) - -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - new File(sourcePath + name).delete(); -\end_layout - -\begin_layout Plain Layout - - Vm.debug("Index file deleted: " + name); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -catch (IOException exception) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - // ... -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Section -Litebase Multiple Languages -\end_layout - -\begin_layout Standard -Litebase supports more than one language. - Up to now, the supported languages are: English (the default language) - and Portuguese. - This feature puts Litebase's messages in your chosen language. - To chose a language, it is just necessary to set the language field: -\end_layout - -\begin_layout Standard - -\family typewriter -LitebaseConnection.language = LitebaseConnection.LANGUAGE_PT; -\end_layout - -\begin_layout Standard -The possible parameters are: -\end_layout - -\begin_layout Standard - -\family typewriter -LitebaseConnection.LANGUAGE_EN -\family default -: English (Default) -\end_layout - -\begin_layout Standard - -\family typewriter -LitebaseConnection.LANGUAGE_PT -\family default -: Portuguese -\end_layout - -\begin_layout Standard -To get the current language, it is only necessary to access the field language: - -\end_layout - -\begin_layout Standard - -\family typewriter -int language = LitebaseConnection.language; -\end_layout - -\begin_layout Section -Limitations and Usage -\end_layout - -\begin_layout Itemize -The LitebaseConnection class is used to issue the SQL command for a specific - set of tables, and is constructed using a creator id. - All tables created by this instance will have the given creator id. -\end_layout - -\begin_layout Itemize -If Litebase is to be used in an applet, the jars used must be signed in - order to be used in a browser. - Otherwise, it won't be possible to create or access tables. -\end_layout - -\begin_layout Itemize -An index is created using the table name and appending a -\family typewriter -$ -\family default - to indices of one attribute or -\family typewriter -& -\family default - to indices of two or more attributes (composed index). - Thus, the table name is limited to 23 characters. - It is possible to create up to 32 composed indices. - -\end_layout - -\begin_layout Itemize -Indices are used during the evaluation of the where clause when: -\end_layout - -\begin_deeper -\begin_layout Itemize -The following operators are used: -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -and -\family default -; -\end_layout - -\begin_layout Itemize - -\family typewriter -or -\family default -; -\end_layout - -\begin_layout Itemize -( ); -\end_layout - -\begin_layout Itemize - -\family typewriter -not -\family default - (are removed internally), and -\end_layout - -\begin_layout Itemize - -\family typewriter -like 'a%' -\family default - (starts with); -\end_layout - -\end_deeper -\begin_layout Itemize -The following operators are not used: -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -like '%a' -\family default - (ends with), and -\end_layout - -\begin_layout Itemize - -\family typewriter -like '%a%' -\family default - (index of). -\end_layout - -\end_deeper -\end_deeper -\begin_layout Itemize -Indices are not used for all the where clause if it uses different boolean - operators ( -\family typewriter -not -\family default -s are not counted as they are removed). -\end_layout - -\begin_layout Itemize -If your where clause is of the form -\family typewriter -A and B and C ... - and Z -\family default -, or -\family typewriter -A or B or C ... - or Z -\family default -, to ensure that the indices will be applied to all columns that have simple - indices, the columns using indices should all be in the rightmost or leftmost - part of the clause. -\end_layout - -\begin_layout Itemize -Litebase does not have the reserved word -\family typewriter -IN -\family default -. - A big OR with all the values in the set must be used instead. -\end_layout - -\begin_layout Itemize -The composed indices are used only when there's an AND operation of all - correspondent fields of the composed index in its creation order, and the - fields use equals operator on comparison. -\end_layout - -\begin_layout Itemize -Indices can also be used to compute -\family typewriter -max() -\family default - and -\family typewriter -min() -\family default - aggregation functions. - In order to use them, the column must have an index or be the first column - of a composed index and the query cannot have group by. - Moreover the query can't have a where clause or its where clause must be - all solved using indices. -\end_layout - -\begin_layout Itemize -Indices may also be used when using -\family typewriter -order by -\family default - or -\family typewriter -group by -\family default - clauses. - In order to use them, the query also can't have a where clause or its where - clause must be all solved using indices. - Furthermore, the query can't have aggregations. - Additionally, if there is only one column to be sorted, it must be a primary - key column, the first column of a composed primary key column or the first - column of an index if it is declare as not null. - If there are more than one column to be sorted, they must be the fist part - of the composed primary key or a composed index if they are declared as - not null. - Notice that columns that are not part of a primary key must be declared - as not null since Litebase indices do not store nulls. - Finally, the order of all columns to be ordered must be the same, either - all of them are sorted in the ascending or in the descending order. - -\end_layout - -\begin_layout Itemize -Indices can be created after a table already contains elements. - Note that the creation of the index can be a slow operation; it can take - some minutes on very big tables. - The best method is to create all indices when there's no data in the table. - However, If you plan to insert lots of data (above 20% of current size, - or above 1000 rows), drop all indices, insert the data, then create them - again. -\end_layout - -\begin_layout Itemize -SQL commands are case insensitive. -\end_layout - -\begin_layout Itemize -Primary Keys and composed primary keys are supported. -\end_layout - -\begin_layout Itemize -Aggregation functions ( -\family typewriter -max() -\family default -, -\family typewriter -min( -\family default -), -\family typewriter -avg() -\family default -, -\family typewriter -sum() -\family default -, and -\family typewriter -count() -\family default -), -\family typewriter -order by -\family default - and -\family typewriter -group by -\family default - are supported. -\end_layout - -\begin_layout Itemize - -\family typewriter -sum() -\family default - and -\family typewriter -avg() -\family default - aggregation functions are not used with -\family typewriter -date -\family default -, -\family typewriter -datetime -\family default -, -\family typewriter -char -\family default -, -\family typewriter -char nocase -\family default -, -\family typewriter -varchar -\family default -, and -\family typewriter -varchar nocase -\family default - type fields. -\end_layout - -\begin_layout Itemize - -\family typewriter -count() -\family default - can only be used with -\family typewriter -* -\family default - as a parameter. -\end_layout - -\begin_layout Itemize -The join operation is supported. - Inner, left and right join are not supported. - The only way to do a join is as the following sample: -\end_layout - -\begin_deeper -\begin_layout Standard - -\family typewriter -select * from table1, table2, tableN ... -\end_layout - -\begin_layout Standard -The performance of a join can vary greatly depending on the order of the - tables and the clauses in the where clause. - So, if a join operation is taking too long, one should try changing these - orders and see if the performance improves. -\end_layout - -\begin_layout Standard -Sometimes, the join speed can be improved if the clauses in the where clause - use simple indices. - Moreover, if there is a restricted clause, this should be the last clause - of the where clause. - Additionally, the order of the clauses must be in such a way that the first - clauses access the smallest tables. - Last but not least, in a clause, the first field being accessed should - be of the smallest table. - These recommendations are some strategies that usually work. - However, some joins usually perform better doing the reverse. - Consequently, if the join is still very slow, one should try reversing - the tables order and where clause order until finding a better result. -\end_layout - -\end_deeper -\begin_layout Itemize -Null and default values are supported. -\end_layout - -\begin_layout Itemize - -\family typewriter -blob -\family default - type is supported. - Notice that blobs must be inserted in a table only via prepared statement, - as a byte array. - It also can't be used in functions, aggregations, -\family typewriter -where -\family default -, -\family typewriter -having -\family default -, -\family typewriter -order by -\family default -, and -\family typewriter -group by -\family default - clauses because it does not make sense to compare blobs. - Therefore, they can't also be indexed. - Moreover, they can't be displayed as a string by Litebase. - Whenever you try to recover a blob field from a table as a string, a null - value is returned instead. -\end_layout - -\begin_layout Itemize - -\family typewriter -varchar -\family default - type is supported. - Internally, -\family typewriter -char -\family default - and -\family typewriter -varchar -\family default - are treated in the same way in order to not let the table files become - to big. - If your application has memory problems, try to reduce SQL strings length - changing field declarations from -\family typewriter -varchar -\family default - to -\family typewriter -char -\family default -. -\end_layout - -\begin_layout Itemize -It is possible to use -\family typewriter -' -\family default - (single quote) inside strings. - Just use -\family typewriter - -\backslash -' -\family default - in a prepared statement or in a SQL string passed by the user. - In a sql string in a TotalCross program, -\family typewriter - -\backslash - -\backslash -' -\family default - must be used instead. -\end_layout - -\begin_layout Itemize -Using prepared statement in batch operations is 3 to 4 times faster than - using direct calls to -\family typewriter -LitebaseConnection.executeUpdate() -\family default -. -\end_layout - -\begin_layout Itemize -A query with no -\family typewriter -where -\family default - clause, using all fields (*) or the table fields in the correct order beginning - with the -\family typewriter -rowid -\family default - is much faster than other queries that return all the rows. - This happens because table data do not need to be searched. - Moreover, no temporary tables are created if the query do not have joins, - aggregations or sortings, which improves performance and memory usage. -\end_layout - -\begin_layout Itemize -The table's file name is prefixed with the creator id. - This is important to let two different programs use the same table name. - Prefixing the table name with the creator id ensures that the files won't - be overwritten (unless they match, which will occur if they don't obey - the rules for creating and defining creator ids). - -\end_layout - -\begin_layout Itemize -It is possible to use multiple connections. - That is, it is possible to access two databases at the same time in different - directories or in the same directory with different creator ids. - When using Android, it is possible to access tables in the flash memory - and in the memory card at the same time. -\end_layout - -\begin_layout Itemize - -\family typewriter -LitebaseConnection.exists() -\family default - does not load the table and indices. - It only tests if the -\family typewriter -.db -\family default - file exists in the current connection path. - This prevents the program to abort if the table and/or indices are corrupted - or are using an older format. - It also works properly with already loaded tables. -\end_layout - -\begin_layout Itemize -Droping table also does not load the table files. - It only searches for the table files and erase them. - This also prevents the program to abort if the table and/or indices are - corrupted or are using the old format. - Moreover, it works properly with already loaded tables. -\end_layout - -\begin_layout Itemize -If Unicode characters are not to be used in the tables, it is possible to - create them in order to use only ASCII characters in string types. - This saves memory, disk space and makes the reading and writing operations - faster. - It is very important to say that a -\family typewriter -.dbo -\family default - that has only strings has its size almost halved if it is stored in the - ASCII mode. - Notice that all the tables of a given connection have their strings stored - in either ASCII or Unicode format, and it is not possible to open a table - with a connection using a different format of string saving nor changing - its format after creation. -\end_layout - -\begin_layout Itemize -If the application demands that no one can find anything meaningful when - opening a table file, simple cryptography can be used. - This makes Litebase operations a little bit slower. - However, information is safer. - Notice that a table that uses cryptography can't be used in a connection - that does not use cryptography and vice-versa. - -\end_layout - -\begin_layout Itemize -Relative paths can't be used with Litebase. - That is, a relative path can't be passed to -\begin_inset Newline newline -\end_inset - - -\family typewriter -Settings.dataPath -\family default - (remember that -\family typewriter -Settings.appPath -\family default - can't be changed!) or as a new connection parameter. - If this is done, an exception will be thrown. - -\end_layout - -\begin_layout Itemize - -\family typewriter -char(1) -\family default -columns can be created, but it is much more efficient to use a short column - with the char ASCII code. -\end_layout - -\begin_layout Itemize -The tables should not be opened at the same time by two different connections. - On Java SE, Windows 32, and Windows Phone 8, a file cannot be opened twice - at the same time. - So, an exception will be thrown if a table is opened at the same time by - two different connections. - This happens because these platforms don't allow a file to be opened twice - at the same time. - However, on Android, Linux, and iOS it is possible to open a file twice - at the same time. - Therefore, if a table is opened at the same time by two different connections, - your application may behave unexpectedly. - Table files should also not be opened while using the tables with Litebase - and vice-versa for the same reason. -\end_layout - -\begin_layout Itemize -There are some limits that must be respected: -\end_layout - -\begin_deeper -\begin_layout Itemize -It is not possible to create a table with more than 254 columns, nor using - more than 254 fields or columns. -\end_layout - -\begin_layout Itemize -A blob field can't be declared to be larger than 10 Mb; -\end_layout - -\begin_layout Itemize -A string field can't be declared to be have more than 65535 characters; -\end_layout - -\begin_layout Itemize -It is not possible to create a table name with more than 23 characters. - This limitation was due to the compatibility with old Palm OS and should - be removed in the near future. -\end_layout - -\begin_layout Itemize -A table can't have more than 32 composed indices; -\end_layout - -\begin_layout Itemize -The -\family typewriter -.dbo -\family default - file can't be greater than 2 Gb. - This will be changed as the PDAs are improved, and -\end_layout - -\begin_layout Itemize -A table with an index can't have more than ~2,6 million index keys. - Again, this will be changed as the PDAs are improved. -\end_layout - -\end_deeper -\begin_layout Standard - -\series bold -It is very important that you take a look at the test cases. -\end_layout - -\begin_layout Subsection -Memory Card Support -\end_layout - -\begin_layout Standard -It is possible to store databases on memory cards on the device that supports - it. - Currently, only Android supports it. - If it is necessary to fetch Litebase tables or logs from an Android device, - it is a good idea to create the data base on the memory card by using the - path -\family typewriter -/sdcard -\family default -, unless the device is rooted and it is possible to fetch the table files - using -\family typewriter -adb -\family default - from the application folder. -\end_layout - -\begin_layout Standard -Although Windows Phone 8 supports a memory card, it's read-only. - You can't create a database on it. - The only way to fetch database files and other application files is using - the Isolated Storage Explorer command-line tool for Windows Phone. - This is inside the Windows Phone SDK. - You can also use it to put files in the application folder on the device. - To know how to use it, read the following tutorial: -\begin_inset CommandInset href -LatexCommand href -name "http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh286408(v=vs.105).aspx" -target "http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh286408(v=vs.105).aspx" - -\end_inset - -. -\end_layout - -\begin_layout Subsection -Using Threads -\end_layout - -\begin_layout Standard -It is possible to use Litebase within threads. - An user can access different connections in different threads. - However, it is highly not recommended to access the same tables and connections - within different threads. - Doing this will probably crash an application. - This was implemented this way to improve execution performance. - In the future, it will be possible to have concurrent access to the tables - as this is needed by many programmers. -\end_layout - -\begin_layout Subsection -Logging and Debugging -\end_layout - -\begin_layout Standard -Litebase supports logging. - It will write to a file all the operations, such as queries, updates, deletes, - inserts, and other methods of the -\family typewriter -LitebaseConnection -\family default - class that are called. - It can help Litebase developers a lot in finding the causes of a bug in - the driver, but it must be used with care, because it will slow down Litebase - and also take much of the device's flash memory (E.G.: a log file of the - -\family typewriter -AllTests -\family default - program takes more than 1 Mb). - So, if a problem is found when using Litebase, follow these steps: -\end_layout - -\begin_layout Enumerate -Add this line to the application before using Litebase: -\family typewriter - -\begin_inset Newline newline -\end_inset - -LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); -\family default - -\begin_inset Newline newline -\end_inset - -if the application does not use threads. - If it uses threads, use -\family typewriter - -\begin_inset Newline newline -\end_inset - -LitebaseConnection.setLogger(LitebaseConnection.getDefaultLogger(); -\end_layout - -\begin_layout Enumerate -Run the application until the error is found. - -\end_layout - -\begin_layout Enumerate -Synchronize the files to the desktop and find the file(s) named as -\family typewriter - -\begin_inset Newline newline -\end_inset - -Litebase_YYYYMMDDHHMMSS.CRTR.LOGS -\family default -, where -\family typewriter -CRTR -\family default - is the creator id of your application. - -\end_layout - -\begin_layout Enumerate -Zip the file(s) and open a support request at SuperWaba CRMDesk system explainin -g what has being done, the error that occurred, which platform and device - that were being used, along with uploading the zipped log file and table - files in a state after and before the execution of the log commands. -\end_layout - -\begin_layout Enumerate -Don't forget to turn off the logging process, by commenting out the line - above, unless it is wanted to fill up all the PDA's space. - To dispose the log, use -\family typewriter -Logger.dispose(true) -\family default -. - It is also necessary to set the Litebase logger to null doing: -\family typewriter - -\begin_inset Newline newline -\end_inset - -LitebaseConnection.logger = null; -\family default - -\begin_inset Newline newline -\end_inset - -if your application does not use threads. - If it uses threads, use -\family typewriter - -\begin_inset Newline newline -\end_inset - -LitebaseConnection.setLogger(null); -\family default - -\begin_inset Newline newline -\end_inset - -Log files are never deleted, so it must be done by the programmer, calling -\begin_inset Newline newline -\end_inset - - -\family typewriter -LitebaseConnection.deleteLogFiles() -\family default - for log files created with -\begin_inset Newline newline -\end_inset - - -\family typewriter -LitebaseConnection.getDefaultLogger() -\family default -. - They are no longer pdb files as it were in previous versions, but plain - text files. - -\end_layout - -\begin_layout Enumerate -The logger can also be something different from a normal text file. - Please check -\family typewriter - -\begin_inset Newline newline -\end_inset - -totalCross.util.Logger -\family default - for more information. -\end_layout - -\begin_layout Enumerate -If it is prefered using logging all the time, the PDA WILL run out of storage - space. - This may cause a Litebase operation failure and inconsistency between files - and tables if using Litebase is not stopped for a while to erase log files - before running out of space. -\end_layout - -\begin_layout Enumerate -To reduce log size, one can omit Litebase operations that don't change the - database. - These includes: -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -LitebaseConnection.executeQuery() -\family default -; -\end_layout - -\begin_layout Itemize - -\family typewriter -LitebaseConnection.getCurrentRowId() -\family default -; -\end_layout - -\begin_layout Itemize - -\family typewriter -LitebaseConnection.getRowCount() -\family default -; -\end_layout - -\begin_layout Itemize - -\family typewriter -LitebaseConnection.getRowCountDeleted() -\end_layout - -\begin_layout Itemize - -\family typewriter -LitebaseConnection.getRowIterator() -\end_layout - -\begin_layout Itemize - -\family typewriter -LitebaseConnection.prepareStatement() -\family default -, and -\end_layout - -\begin_layout Itemize - -\family typewriter -PreparedStatement.executeQuery() -\family default -. -\end_layout - -\begin_layout Standard -To do that, it is only necessary to set -\family typewriter -LitebaseConnection.logOnlyChanges -\family default - to true. - This is interesting when some error occurs that corrupts the database after - a long system run. - Since the operations listed above don't change the database, they can't - cause table or index corruption (the most usual Litebase problem). - -\end_layout - -\end_deeper -\begin_layout Enumerate -If the default logger is used, it will be saved in -\family typewriter -Settings.appPath -\family default -. - On Android, it is not always possible to fetch the log file unless changing - some access permissions or even rooting the device. - Since this may be undesirable, the log file can be saved on the external - card so that it can be easily fetched. - In order to change its place, it is necessary to create it using -\family typewriter -totalCross.util.Logger -\family default -. - Here is an example for Android using the default path for the external - card: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -File log = new File(Convert.appendPath("/sdcard/", "Litebase_" + new Time().getTim -eLong() + '.' + Settings.applicationId + ".LOGS"), File.CREATE_EMPTY, -1); -\end_layout - -\begin_layout Plain Layout - -LitebaseConnection.logger = Logger.getLogger("litebase", -1, log); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection -Compatibility -\end_layout - -\begin_layout Standard -Litebase needs TotalCross to run on the devices. - Moreover, one specific Litebase version does not run on all TotalCross - versions. - So, it is not recommended to run a Litebase version on incompatible TotalCross - version. - This can cause strange errors or even an application crash. - The table below shows the ensured compatibility between TotalCross and - Litebase versions. -\end_layout - -\begin_layout Standard -\align center -\begin_inset Tabular - - - - - - -\begin_inset Text - -\begin_layout Plain Layout -TotalCross -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Litebase -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.20 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.20 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.21 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.21 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.22 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.22 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.23 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.23 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.24 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.24 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.25 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.25 2.26 2.26a -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.27 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.27 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.28 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.28 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.29 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.29 2.30 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.30 to 1.37 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.30 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.38 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.38 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.50 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.50 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.51 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.51 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.52 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.52 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.60 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.60 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.61 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.61 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.62 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.62 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.63 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.63 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.64 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.64 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.65 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.65 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.66 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.66 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.67 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.67 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -1.7 to 2.0 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.7 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -3.0 -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -2.8 -\end_layout - -\end_inset - - - - -\end_inset - - -\end_layout - -\begin_layout Standard -Please notice that all indices files must be erased in order to use Litebase - tables created with version 2.67 with Litebase 2.7/2.8 and vice-versa. - Moreover, tables using cryptography created in version 2.7/2.8 can't be used - with version 2.67. - -\end_layout - -\begin_layout Section -Litebase SQL Functions -\end_layout - -\begin_layout Standard -In this section, it is possible to find all SQL data type functions accepted - by Litebase. -\end_layout - -\begin_layout Subsection - -\family typewriter -short -\family default -, -\family typewriter -int -\family default -, -\family typewriter -long -\family default -, -\family typewriter -float -\family default -, and -\family typewriter -double -\family default - functions -\end_layout - -\begin_layout Standard -\begin_inset Tabular - - - - - - - - -\begin_inset Text - -\begin_layout Plain Layout -Function -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Return type -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Description -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ResultSet -\family default - method -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ABS(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\align center -Returns the absolute value of the given -\family typewriter -short -\family default - field. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ABS(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -INT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Returns the absolute value of the given -\family typewriter -int -\family default - field. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getInt() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ABS(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -LONG -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Returns the absolute value of the given -\family typewriter -long -\family default - field. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getLong() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ABS(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -FLOAT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Returns the absolute value of the given -\family typewriter -float -\family default - field. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getFloat() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ABS(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -DOUBLE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Returns the absolute value of the given -\family typewriter -double -\family default - field. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getDouble() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\end_inset - - -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// First creates and fills the table. -\end_layout - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance(); -\end_layout - -\begin_layout Plain Layout - -driver.execute("create table person(name char(16), amount int, amount1 short, - amount2 long, amount3 float, amount4 double, birth Date, years DateTime)"); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Note: These inserts statements will be used by all examples about SQL - functions. -\end_layout - -\begin_layout Plain Layout - -driver.executeUpdate("insert into person values ('Renato Novais', -12, -1, - -100, -1.2, -456.0, ' 2007/5-3 ', ' 2007/11-2 12:08:01:234 ')"); -\end_layout - -\begin_layout Plain Layout - -driver.executeUpdate("insert into person values ('indira gomes',13, -8, -25, - 5.2, -154.0, '2006/7/8 ', '2006/08-21 0:08')"); -\end_layout - -\begin_layout Plain Layout - -driver.executeUpdate("insert into person values ('Lucas Novais', -20, -456, - 48, -5.9, -954.2, '2008/4/6', ' 2008/06/06 13:45 ')"); -\end_layout - -\begin_layout Plain Layout - -driver.executeUpdate("insert into person values ('Zenes Lima', -15, -54, - -5698, -8.3, -456.5, '2005/9/12 ', '2005/01-4 1:50')"); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Selects with functions. - -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select abs(amount) as a0, abs(amount1) as a1, abs(amoun -t2) as a2, abs(amount3) as a3, abs(amount4) as a4 from person -\end_layout - -\begin_layout Plain Layout - -where abs(amount)>13"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 2. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getInt(1); // Returns 20. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 456. - -\end_layout - -\begin_layout Plain Layout - -rs.getLong(3); // Returns 48. - -\end_layout - -\begin_layout Plain Layout - -rs.getFloat(4); // Returns 5.9. - -\end_layout - -\begin_layout Plain Layout - -rs.getDouble(5); // Returns 954.2. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getInt(1); // Returns 15. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 54. - -\end_layout - -\begin_layout Plain Layout - -rs.getLong(3); // Returns 5698 . - -\end_layout - -\begin_layout Plain Layout - -rs.getFloat(4); // Returns 8.3. - -\end_layout - -\begin_layout Plain Layout - -rs.getDouble(5); // Returns 456.5. - -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -char -\family default -, -\family typewriter -varchar -\family default -, -\family typewriter -char nocase -\family default -, and -\family typewriter -varchar nocase -\family default - functions -\end_layout - -\begin_layout Standard -\begin_inset Tabular - - - - - - - - -\begin_inset Text - -\begin_layout Plain Layout -Function -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Return type -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Description -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ResultSet -\family default - method -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -UPPER(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -CHAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\align center -Converts all lowercase letters in a character string to uppercase. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getChars() -\family default - or -\family typewriter -getString() -\family default - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -LOWER(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -CHAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Converts all uppercase letters in a character string to lowercase. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getChars() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\end_inset - - -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// Selects with functions. - -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select amount, abs(amount) as a1, name, lower(name) - as u1, upper(name) as u2 from person where abs(amount)>12 and UPPER(name) - > 'INDIRA GOMES'"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 2. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getInt(1); // Returns -20. - -\end_layout - -\begin_layout Plain Layout - -rs.getInt(2); // Returns 20. - -\end_layout - -\begin_layout Plain Layout - -rs.getString(3); // Returns 'Lucas Novais'. - -\end_layout - -\begin_layout Plain Layout - -rs.getString(4); // Returns 'lucas novais'. - -\end_layout - -\begin_layout Plain Layout - -rs.getString(5); // Returns 'LUCAS NOVAIS'. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getInt(1); // Returns -15. - -\end_layout - -\begin_layout Plain Layout - -rs.getInt(2); // Returns 15. - -\end_layout - -\begin_layout Plain Layout - -rs.getString(3); // Returns 'Zenes Lima'. - -\end_layout - -\begin_layout Plain Layout - -rs.getString(4); // Returns 'zenes lima'. - -\end_layout - -\begin_layout Plain Layout - -rs.getString(5); // Returns 'ZENES LIMA'. - -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -date -\family default - and -\family typewriter -datetime -\family default - functions -\end_layout - -\begin_layout Standard -\begin_inset Tabular - - - - - - - - -\begin_inset Text - -\begin_layout Plain Layout -Function -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Return type -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Description -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -ResultSet -\family default - method -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -YEAR(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\align center -Gets subfield equivalent to year. - Field CAN be -\family typewriter -date -\family default - or -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -MONTH(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Gets subfield equivalent to year. - Field CAN be -\family typewriter -date -\family default - or -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -DAY(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Gets subfield equivalent to hour. - Field MUST be -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -HOUR(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Gets subfield equivalent to hour. - Field MUST be -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -MINUTE(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Gets subfield equivalent to hour. - Field MUST be -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SECOND(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Gets subfield equivalent to hour. - Field MUST be -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -MILLIS(field) -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Gets subfield equivalent to hour. - Field MUST be -\family typewriter -datetime -\family default -. -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\family typewriter -getShort() -\family default - or -\family typewriter -getString() -\end_layout - -\end_inset - - - - -\end_inset - - -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// Selects with functions. - -\end_layout - -\begin_layout Plain Layout - -ResultSet rs = driver.executeQuery("Select month(years) as mon1, years from - person"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 4. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); rs.getShort(1); // Returns 11. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); rs.getShort(1); // Returns 8. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); rs.getShort(1); // Returns 6. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); rs.getShort(1); // Returns 1. - -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select year(years) as y1, years from person where - day(years) >= 6"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 2. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); rs.getShort(1); // Returns 2006. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); rs.getShort(1); // Returns 2008. - -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select hour(years) as h1, day(birth) as d1 from - person where month(birth) != 7 and hour(years) != 0"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 3. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 12. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 3. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 13. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 6. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 1. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 12. - -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select millis(years) as mil1, minute(years) as - sec1 from person where birth > '2005/9-12'"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 3. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 234. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 8. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 0. - -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 8. - -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 0. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 45. -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select year(birth) as y1, month(birth) as m1 day(birth) - as d1 from person where year(birth) = 2005"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 1. -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 2005. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 9. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(3); // Returns 12. -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select hour(years) as h1, minute(years) as m1 second(ye -ars) as d1 from person where hour(years) >= 12"); -\end_layout - -\begin_layout Plain Layout - -rs.getRowCount(); // Returns 2. -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 12. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 8. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(3); // Returns 1. -\end_layout - -\begin_layout Plain Layout - -rs.next(); -\end_layout - -\begin_layout Plain Layout - -rs.getShort(1); // Returns 13. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(2); // Returns 45. -\end_layout - -\begin_layout Plain Layout - -rs.getShort(3); // Returns 0. -\end_layout - -\begin_layout Plain Layout - -rs.close(); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Throws exception: "Incompatible data type for the function call: millis", - -\end_layout - -\begin_layout Plain Layout - -// because birth is date type and millis is a function applied only for - datetime type. -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select millis(birth) as mil, years from person"); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Throws exception: "Incompatible data type for the function call: second". -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select year(birth) as y1, month(birth) as m1, day(birth -) as d1 from person where second(birth) = 234"); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Throws exception: "An alias is required for the aggregate function column. - Error position: 25." -\end_layout - -\begin_layout Plain Layout - -rs = driver.executeQuery("Select month(birth) from person "); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Section -Litebase Reserved Words -\end_layout - -\begin_layout Standard -Litebase, starting from version 2.0, has reserved words. - So, it is not possible to use these words as identifiers. - -\end_layout - -\begin_layout Standard -In the table below there are the reserved words from SQL 2003 standard and - from Litebase. - Many words from SQL 2003 standard are not in Litebase reserved words list, - but is is strongly recommended that these words are not used, because in - a near future they may become a Litebase reserved word. - -\end_layout - -\begin_layout Standard -Although -\family typewriter -distinct -\family default - is a reserved word, it is not currently used by Litebase. - -\family typewriter -group by -\family default - with no aggregated function should be used instead to simulate -\family typewriter -distinct -\family default - behavior. -\end_layout - -\begin_layout Standard -\begin_inset Tabular - - - - - - - -\begin_inset Text - -\begin_layout Plain Layout -SQL word -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -Litebase -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -SQL 2003 -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ABS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ADD -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ALL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ALLOCATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ALTER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -AND -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ANY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ARE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ARRAY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -AS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ASC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ASENSITIVE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ASYMMETRIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -AT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ATOMIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -AUTHORIZATION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -AVG -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BEGIN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BETWEEN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BIGINT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BINARY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BLOB -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BOOLEAN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BOTH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -BY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CALL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CALLED -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CASCADED -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CASE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CAST -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CHAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CHARACTER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CHECK -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CLOB -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CLOSE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -COLLATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -COLUMN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -COMMIT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CONDITION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CONNECT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CONSTRAINT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CONTINUE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ORRESPONDING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -COUNT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CREATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CROSS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CUBE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_DATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_DEFAULT_TRANSFORM_GROUP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_PATH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_ROLE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_TIME -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_TIMESTAMP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_TRANSFORM_GROUP_FOR_TYPE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURRENT_USER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CURSOR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -CYCLE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DATETIME -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DAY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DEALLOCATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DEC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DECIMAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DECLARE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DEFAULT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DELETE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DEREF -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DESC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DESCRIBE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DETERMINISTIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DISCONNECT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DISTINCT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DO -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DOMAIN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DOUBLE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DROP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -DYNAMIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EACH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ELEMENT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ELSE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ELSEIF -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -END -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ESCAPE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EXCEPT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EXEC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EXECUTE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EXISTS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EXIT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -EXTERNAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FALSE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FETCH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FILTER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FLOAT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FOR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FOREIGN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FREE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FROM -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FULL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -FUNCTION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -GET -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -GLOBAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -GRANT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -GROUP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -GROUPING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -HANDLER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -HAVING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -HOLD -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -HOUR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -IDENTITY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -IF -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -IMMEDIATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -IN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INDEX -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INDICATOR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INNER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INOUT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INPUT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INSENSITIVE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INSERT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INTEGER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INTERSECT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INTERVAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -INTO -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -IS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ITERATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -JOIN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -KEY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LANGUAGE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LARGE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LATERAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LEADING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LEAVE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LEFT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LIKE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LOCAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LOCALTIME -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LOCALTIMESTAMP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LONG -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LOOP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -LOWER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MATCH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MAX -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MEMBER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MERGE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -METHOD -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MILLIS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MIN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MINUTE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MODIFIES -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MODIFY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MODULE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -MONTH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NATIONAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NATURAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NCHAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NCLOB -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NEW -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NO -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NOCASE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NONE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NOT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NULL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -NUMERIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OF -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OLD -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ON -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ONLY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OPEN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ORDER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OUT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OUTER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OUTPUT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OVER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -OVERLAPS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -PARAMETER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -PARTITION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -PRECISION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -PREPARE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -PRIMARY -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -PROCEDURE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RANGE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -READS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -REAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RECURSIVE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -REF -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -REFERENCES -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -REFERENCING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RELEASE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RENAME -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -REPEAT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RESIGNAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RESULT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RETURN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RETURNS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -REVOKE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -RIGHT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ROLLBACK -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ROLLUP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ROW -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -ROWS -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SAVEPOINT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SCOPE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SCROLL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SEARCH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SECOND -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SELECT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SENSITIVE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SESSION_USER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SET -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SHORT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SIGNAL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SIMILAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SMALLINT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SOME -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SUM -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SPECIFIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SPECIFICTYPE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SQL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SQLEXCEPTION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SQLSTATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SQLWARNING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -START -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -STATIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SUBMULTISET -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SYMMETRIC -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SYSTEM -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -SYSTEM_USER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TABLE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TABLESAMPLE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -THEN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TIME -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TIMESTAMP -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TIMEZONE_HOUR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TIMEZONE_MINUTE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TO -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TRAILING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TRANSLATION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TREAT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TRIGGER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -TRUE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UNDO -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UNION -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UNIQUE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UNKNOWN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UNNEST -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UNTIL -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UPDATE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -UPPER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -USER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -USING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -VALUE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -VALUES -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -VARCHAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -VARYING -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WHEN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WHENEVER -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WHERE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WHILE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WINDOW -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WITH -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WITHIN -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WITHOUT -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WORK -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -WRITE -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -YEAR -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -reserved -\end_layout - -\end_inset - - - - -\end_inset - - -\end_layout - -\begin_layout Section - -\family typewriter -LitebaseConnection -\family default - Class -\end_layout - -\begin_layout Standard -This class is the one used to issue SQL commands. - It cannot be directly instantiated, only using the -\family typewriter -getInstance() -\family default - methods. -\end_layout - -\begin_layout Standard -If one tries to issue methods in an already closed connection, an -\family typewriter -IllegalStateException -\family default - will be thrown. - A -\family typewriter -SQLParserException -\family default - will be thrown whenever a parser error, invalid number, date, or time occurs. - Moreover, a -\family typewriter -DriverException -\family default - will be thrown whenever an I -\backslash -O problem or other error occurs, such as accessing invalid column or table - names. - In order to make this text cleaner, these kind of exception will be omitted - in the methods description. -\end_layout - -\begin_layout Subsection - -\family typewriter -getInstance() -\end_layout - -\begin_layout Standard - -\family typewriter -public static LitebaseConnection -\series bold -getInstance -\series default -() -\end_layout - -\begin_layout Standard -It creates a -\family typewriter -LitebaseConnection -\family default - for the default application id, storing the database in the main secondary - storage memory. - This method avoids the creation of more than one instance with the same - creator id, path, and thread, which would lead to performance and memory - problems. - -\end_layout - -\begin_layout Standard -This is the same of doing -\begin_inset Newline newline -\end_inset - - -\family typewriter -LitebaseConnection.getInstance(Settings.applicationId); -\end_layout - -\begin_layout Subsection - -\family typewriter -getInstance() -\end_layout - -\begin_layout Standard - -\family typewriter -public static LitebaseConnection -\series bold -getInstance -\series default -(String appCrid) -\end_layout - -\begin_layout Standard -It creates a -\family typewriter -LitebaseConnection -\family default - for the given creator id, which must be 4 characters long and may (or not) - be the same one of your application's, storing the database in the main - secondary storage memory. - This method avoids the creation of more than one instance with the same - creator id, path, and thread, which would lead to performance and memory - problems. - If the application id is not 4 characters long, a -\family typewriter -DriverException -\family default - will be thrown. -\end_layout - -\begin_layout Subsection - -\family typewriter -getInstance() -\begin_inset CommandInset label -LatexCommand label -name "sub:getInstance()" - -\end_inset - - -\end_layout - -\begin_layout Standard - -\family typewriter -public static LitebaseConnection -\series bold -getInstance -\series default -(String appCrid, -\begin_inset Newline newline -\end_inset - -String params) -\end_layout - -\begin_layout Standard -It creates a -\family typewriter -LitebaseConnection -\family default - for the given creator id, which must be 4 characters long and may (or not) - be the same one of your application's, and with the given connection parameter - list. - This method avoids the creation of more than one instance with the same - creator id, path, and thread, which would lead to performance and memory - problems. - If the application id is not 4 characters long, a -\family typewriter -DriverException -\family default - will be thrown. -\end_layout - -\begin_layout Standard -The second parameter, -\family typewriter -params -\family default -, can be the path where it is desired to access the database. - It must be an absolute path, otherwise a -\family typewriter -DriverException -\family default - will be thrown. - If -\family typewriter -null -\family default - is passed as this parameter, the default folder will be used. - For Java, the default path is -\family typewriter -Settings.dataPath -\family default -. - For the other systems, the default path is given by -\family typewriter -Settings.appPath -\family default -. -\end_layout - -\begin_layout Standard -To use ASCII tables or cryptography, the parameters must have the format: - -\family typewriter - -\begin_inset Newline newline -\end_inset - -chars_type = type; path = path_name[; crypto] -\family default - -\begin_inset Newline newline -\end_inset - -where -\family typewriter -chars_type -\family default - can be -\family typewriter -ascii -\family default - to store ASCII strings or -\family typewriter -unicode -\family default - to use unicode strings in the tables, path has the same format of the preceding - case, and -\family typewriter -crypto -\family default - must be used if the tables of the connection use cryptography. - It is also possible to use just one parameter ( -\family typewriter -chars_type = type -\family default -, -\family typewriter -path = path_name -\family default -, or -\family typewriter -crypto -\family default -) or combinations of two parameters. - The parameters can be entered in any order. - If only the path is passed, the default format is unicode and no cryptography - is used. - This is also the format used for the above versions of this method. -\end_layout - -\begin_layout Standard -Note: although -\family typewriter -getInstance() -\family default - is a singleton, it is not recommended to call this method more than once - for the same connection in the same thread. - This will only make your application inefficient due to many method calls. - It is much better to hook the connection with Litebase in a static variable - or to pass it as a parameter. -\end_layout - -\begin_layout Standard -A very important thing to be noticed is that two different connections will - be created on Java SE if one of the -\family typewriter -getInstance() -\family default - calls is in the constructor of the -\family typewriter -MainWindown -\family default - or in a method called by it and the other call is in a -\family typewriter -initUI() -\family default -, -\family typewriter -onEvent() -\family default -, or in its sub-methods. -\end_layout - -\begin_layout Subsection - -\family typewriter -getSourcePath() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getSourcePath -\series default -() -\end_layout - -\begin_layout Standard -Returns the source path used by the Litebase connection. - This path is where the tables of this connection are stored. - -\end_layout - -\begin_layout Subsection - -\family typewriter -execute() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -execute -\series default -(String sql) -\end_layout - -\begin_layout Standard -Used to execute a create table or create index SQL commands. - -\end_layout - -\begin_layout Standard -The index can be created after data was added to the table. - -\end_layout - -\begin_layout Standard -An -\family typewriter -AlreadyCreatedException -\family default - may be thrown if the index/table was already created. -\end_layout - -\begin_layout Standard -The following commands are supported: -\end_layout - -\begin_layout Itemize - -\family typewriter -CREATE TABLE table_name ({column_name column_data_type[(type_size -\begin_inset Newline newline -\end_inset - - [multiplier])] [PRIMARY KEY] [DEFAULT value_default] [NOT NULL]} -\begin_inset Newline newline -\end_inset - - [,...] [, PRIMARY KEY (pk_column_name [,...])]) -\end_layout - -\begin_deeper -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Description -\end_layout - -\begin_layout Itemize -Creates the table with the given column definitions. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table to be created. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name of the columns to be created. - One or more. - Cannot have space, neither has a size limit. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_data_type -\family default -: the data type of the column. - Must be one of the following (case insensitive): -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -SHORT -\family default -: represents the Java short type. - Ranges from -32768 to +32767. -\end_layout - -\begin_layout Itemize - -\family typewriter -INT -\family default -: represents the Java integer type. - Ranges from -2.147.483.648 to +2.147.483.647. -\end_layout - -\begin_layout Itemize - -\family typewriter -LONG -\family default -: represents the Java long type. - Ranges from -9.223.372.036.854.775.808 to +9.223.372.036.854.775.807. -\end_layout - -\begin_layout Itemize - -\family typewriter -FLOAT -\family default -: represents the Java float type. - Ranges from -3,40292347E+38 to +3,40292347E+38 on iPhone and from 1.4E-45 - to +3,40292347E+38 on the other platforms. -\end_layout - -\begin_layout Itemize - -\family typewriter -DOUBLE -\family default -: represents the Java double type. - Ranges from 2.2250738585072014E-308 to +1,79769313486231570E+308 on iPhone - and from 4.9E-324 to -\begin_inset Newline newline -\end_inset - -+1,79769313486231570E+308 on the other platforms. - When the column is of type -\family typewriter -LONG -\family default -, it is necessary to append a suffix L (or l) to the number. - E.G.: 20050411095951L. - Also, when using -\family typewriter -FLOAT -\family default - types, it is needed to suffix it with a F (or f), otherwise the default - type will be -\family typewriter -DOUBLE -\family default -. -\end_layout - -\begin_layout Itemize - -\family typewriter -CHAR -\family default -: represents a case sensitive char array (each char range -\family typewriter -u0000 -\family default - to -\family typewriter - -\backslash -uFFFF -\family default -). - This is the one column type that must have the type size specified, from - 1 to 65535. - E.G.: -\family typewriter -char(1) -\family default -, -\family typewriter -char(2000) -\family default -, etc. -\end_layout - -\begin_layout Itemize - -\family typewriter -CHAR NOCASE -\family default -: equivalent to -\family typewriter -CHAR -\family default -, but searches are case insensitive, making the searches slower. - E.G.: -\family typewriter -char (20) nocase -\family default -. -\end_layout - -\begin_layout Itemize - -\family typewriter -VARCHAR -\family default -: internally it is equivalent to -\family typewriter -CHAR -\family default -. - -\end_layout - -\begin_layout Itemize - -\family typewriter -VARCHAR NOCASE -\family default -: internally it is equivalent to -\family typewriter -CHAR NOCASE -\family default -. - -\end_layout - -\begin_layout Itemize - -\family typewriter -DATE -\family default -: represents the Java/TotalCross -\family typewriter -Date -\family default - type. - Ranges from all valid dates between 1000/01/01 and 2999/12/31. - -\end_layout - -\begin_deeper -\begin_layout Itemize -A date 'YYYY/MM/DD' (YYYY = year, MM = Month, DD = Day) is stored as a integer - YYYYMMDD. -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -DATETIME -\family default -: represents the Java/TotalCross -\family typewriter -Time -\family default - type. - Ranges from all possible dates and times with valid dates. - It is stored as two integers. - -\end_layout - -\begin_deeper -\begin_layout Itemize -the first integer represents a date 'YYYY/MM/DD' (YYYY = year, MM = Month, - DD = Day) and is stored as a integer YYYYMMDD. -\end_layout - -\begin_layout Itemize -the second, a time 'HH:MM:SS:ZZZ' (HH = hour, MM = minutes, SS = seconds, - ZZZ = millis) and is stored as a integer HHMMSSZZZ. -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -BLOB -\family default -: represents an array of bytes. - It can be a picture or a video, for example. - It has also a multiplier, which can be K (kilo) or M (mega). - Its total size cannot be greater than 10 Mb. - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -type_size -\family default -: used only for the -\family typewriter -CHAR -\family default -, -\family typewriter -CHAR NOCASE -\family default -, -\family typewriter -VARCHAR -\family default -, -\family typewriter -VARCHAR NOCASE -\family default -, and -\family typewriter -BLOB -\family default - column types. - See above. -\end_layout - -\begin_layout Itemize - -\family typewriter -primary key -\family default -: determines that this column is the primary key one (only for one column - of the table). -\end_layout - -\begin_layout Itemize - -\family typewriter -value_default -\family default -: the default value for the field. - It must be of the same type of the field. -\end_layout - -\begin_layout Itemize - -\family typewriter -NOT NULL -\family default - indicates that the column can't have a -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Itemize - -\family typewriter -pk_column_name -\family default -: the column names to specify a composed primary key. -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -CREATE TABLE person (name char(40) nocase primary key, birthday int default - 20 not null, salary double default 2325.00, gender char(1), married char(3), - age short) -\end_layout - -\begin_layout Plain Layout - -CREATE TABLE person2 (name char(40) nocase, birthday int default 20 not - null, salary double default 2325.00, gender char(1), married char(3), age - short, primary key (name, birthday)) -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -More about -\family typewriter -DATE -\family default - and -\family typewriter -DATETIME -\end_layout - -\end_deeper -\begin_layout Itemize -Use quotes for this data type. -\end_layout - -\begin_deeper -\begin_layout Itemize -White spaces in the beginning or in the end are not considered. -\end_layout - -\begin_layout Itemize -The date must be inserted in the one of following formats: -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -DATE -\family default - format -\end_layout - -\begin_deeper -\begin_layout Itemize -YYYY/MM/DD, YY/MM/DD, YY-MM-DD, YY-MM-DD -\end_layout - -\end_deeper -\end_deeper -\begin_layout Itemize -The -\family typewriter -DATETIME -\family default - must be inserted in one of the following formats: -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -DATE -\family default - format + space + -\family typewriter -TIME -\family default - format -\end_layout - -\begin_layout Itemize - -\family typewriter -TIME -\family default - format -\end_layout - -\begin_deeper -\begin_layout Itemize -HH:MM:SS:ZZZ, HH:MM:SS, HH:MM, HH, H -\end_layout - -\end_deeper -\end_deeper -\begin_layout Itemize -The select statements recoveries -\family typewriter -DATE -\family default - and -\family typewriter -DATETIME -\family default - according with -\family typewriter -appSettings -\family default -. -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -create table person(name char(16), age int, birth date primary key, years - DateTime) -\end_layout - -\begin_layout Plain Layout - -insert into person values ('renato novais', 12, '2005/9-12', '2006/08-21 - 12:08') -\end_layout - -\begin_layout Plain Layout - -insert into person values ('indira gomes', 13, ' 2005/9-12', '05-4/3 1:8:59' -\end_layout - -\begin_layout Plain Layout - -insert into person values ('Zenes Oliveira', 20, '07/9-13', '2006/08-21 - 13:08:59:431 ' -\end_layout - -\begin_layout Plain Layout - -Select name, age, birth, years from person -\end_layout - -\begin_layout Plain Layout - -Select name, age, birth, years from person where birth = ' 2005/09/12' -\end_layout - -\begin_layout Plain Layout - -Select name, age, birth, years from person where years != '05-4/3 01:8:59 - ' -\end_layout - -\begin_layout Plain Layout - -Select min(years) as abirth from person -\end_layout - -\begin_layout Plain Layout - -Select max(birth) as abirth from person -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -CREATE INDEX index_name ON table_name(column_name [,...]) -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Creates an index for the given table column. - -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -index_name -\family default -: the name of the index. - -\begin_inset Newline newline -\end_inset - -Obs.: The -\family typewriter -index_name -\family default - is ignored and formed internally (but must be included in the SQL command). -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of table. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name of column to be created the index. - Here it is possible to specify one or more column names. - Two or more column names generates a composed index. -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -CREATE INDEX indexperson ON person(birthday) -\end_layout - -\begin_layout Plain Layout - -CREATE INDEX indexperson2 ON person(name, birthday) -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Subsection - -\family typewriter -executeUpdate() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -executeUpdate -\series default -(String sql) -\end_layout - -\begin_layout Standard -Used to execute updates in a table (insert, delete, update, alter table, - and drop). - -\end_layout - -\begin_layout Standard -The following commands are supported: -\end_layout - -\begin_layout Itemize - -\family typewriter -ALTER TABLE table_name DROP primary key -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Deletes the primary key of a table. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Standard -Throws -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: if the table does not have a primary key. -\end_layout - -\begin_layout Standard -Example -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ALTER TABLE person DROP primary key -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -ALTER TABLE table_name ADD primary key(column_name [,...]) -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Adds a primary key to a table. -\end_layout - -\end_deeper -\begin_layout Itemize -Parameters -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name(s) of the column(s) to be created the primary key. -\end_layout - -\begin_layout Standard -Throws -\end_layout - -\begin_layout Itemize - -\family typewriter -PrimaryKeyViolationException -\family default -: if there is a repeated or null key in the table. -\end_layout - -\begin_layout Standard -Example -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ALTER TABLE person ADD primary key (salary) -\end_layout - -\begin_layout Plain Layout - -ALTER TABLE person ADD primary key (name, salary) -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -ALTER TABLE table_name RENAME TO new_table_name -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Renames a table from -\family typewriter -table_name -\family default - to -\family typewriter -new_table_name -\family default -. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table to be changed. -\end_layout - -\begin_layout Itemize - -\family typewriter -new_table_name -\family default -: the new name of the table. -\end_layout - -\begin_layout Standard -Throws -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: if the new table name already exists. -\end_layout - -\begin_layout Standard -Example -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ALTER TABLE person RENAME TO employee -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -ALTER TABLE table_name RENAME column_name TO new_column_name -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Renames a column of a table from -\family typewriter -column_name -\family default - to -\family typewriter -new_column_name -\family default -. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table where the column will be changed. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name of the column to be changed. -\end_layout - -\begin_layout Itemize - -\family typewriter -new_column_name -\family default -: the new name of the column. -\end_layout - -\begin_layout Standard -Throws -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: if the new table already exists. -\end_layout - -\begin_layout Standard -Example -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ALTER TABLE person RENAME age TO years -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -ALTER TABLE table_name ADD column_name column_data_type [(type_size [multiplier] -)][DEFAULT value_default] [NOT NULL] -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Adds a column to a table. - The syntax is similar to a column declaration when a table is being created. - However, it is not possible to add a column declaring it a primary key, - because all rows will have the same value, being null or equal to the declared - default value, which violates the primary key constraints. - Moreover, if the column is declared as not null, there must be a default - value different from null, because otherwise all the rows would have a - null value in the new column. -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -DROP TABLE table_name -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Deletes the table and all associated indices. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Standard -Example -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -DROP TABLE person -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -DROP INDEX {column_name [,...] | *} on table_name -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Deletes an index from the given table name. - The index can be specified by its name(s) of the correspondent column(s) - or by a wild card symbol (*), which deletes all indices associated with - the table. - ATTENTION: The index associated to the primary key is not deleted. - So, if it is desired to delete this, one must use the -\family typewriter -alter table -\family default - statement. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name of the column. - You can specify one or more fields name. -\end_layout - -\begin_layout Itemize - -\family typewriter -* -\family default - : specifies to delete all indices associated to the table, except the primary - key index. - -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -DROP INDEX salary ON person -\end_layout - -\begin_layout Plain Layout - -DROP INDEX * ON person -\end_layout - -\begin_layout Plain Layout - -DROP INDEX name, salary ON person -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -INSERT INTO table_name [(column_name [, ...])] {VALUES ({values} [, ...])} -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Inserts the given row into the table. - The target column names may be listed in any order. - The driver assumes -\family typewriter -default -\family default - values if you declared it. - It is possible to insert -\family typewriter -null -\family default - too, but only if the field wasn't declared as -\family typewriter -not null -\family default -. - Note that it is necessary to give the column names only if their declared - order is changed. - If no list of column names is given, the default is all the columns of - the table in their declared order. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name of the column (remember that it is possible to specify either - ALL or ANY). -\end_layout - -\begin_layout Itemize - -\family typewriter -values -\family default -: a value to assign to the corresponding column. -\end_layout - -\begin_layout Standard -Throws -\end_layout - -\begin_layout Itemize - -\family typewriter -PrimaryKeyViolationException -\family default -: if there is a repeated or null key in the table. -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -INSERT INTO person (region, age, salary, married, birthday, gender, name) - VALUES (1, 25, 10000, 'no', 19790606, 'M', 'Renato Novais') -\end_layout - -\begin_layout Plain Layout - -INSERT INTO person VALUES ('Indira Gomes', 19810228, 6000, 'F', 'no', 20, - 2) -\end_layout - -\begin_layout Plain Layout - -INSERT INTO person VALUES ('Cacul', 19800806, 5000, 'M', 'yes',25, 5) -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Fields region, salary, married, birthday, and gender will be filled -\end_layout - -\begin_layout Plain Layout - -// with default values (if you declared them) or null value if permitted. - -\end_layout - -\begin_layout Plain Layout - -INSERT INTO person(name, age) VALUES ('Caio', null) -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -UPDATE table_name SET column_name = value [,...][WHERE condition] -\family default - -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Changes the values of the specified columns in all rows that satisfy the - condition. - Only the columns to be modified need to be mentioned in the -\family typewriter -set -\family default - clause; columns not explicitly modified retain their previous values. - It is possible to specify the condition in the -\family typewriter -where -\family default - clause to determine which rows will be modified. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: the name of the column. -\end_layout - -\begin_layout Itemize - -\family typewriter -expression -\family default -: an expression to assign to the column. - -\end_layout - -\begin_layout Itemize - -\family typewriter -condition -\family default -: an expression that returns a value of type boolean. - Only rows for which this expression returns true will be updated. - -\end_layout - -\begin_layout Standard -Throws -\end_layout - -\begin_layout Itemize - -\family typewriter -PrimaryKeyViolationException -\family default -: if there is a repeated or null key in the table. -\end_layout - -\begin_layout Standard -Example -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -UPDATE person SET salary = 8000 WHERE age = 20 or age is null -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize - -\family typewriter -DELETE FROM table_name [WHERE condition] -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize -Deletes a row or a set of rows in agreement with conditions in -\family typewriter -where -\family default - clause. - If the condition is omitted, the whole table is deleted, but its structure - is preserved. - The key word -\family typewriter -from -\family default - is optional. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name of the table. -\end_layout - -\begin_layout Itemize - -\family typewriter -condition -\family default -: an expression that returns a value of type boolean. - Only rows for which this expression returns true will be deleted. - -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -DELETE FROM person WHERE name = 'Cacul' -\end_layout - -\begin_layout Plain Layout - -DELETE person WHERE age is not null -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Subsection - -\family typewriter -executeQuery() -\end_layout - -\begin_layout Standard - -\family typewriter -public ResultSet -\series bold -executeQuery -\series default -(String sql) -\end_layout - -\begin_layout Standard -Used to execute queries in a table. - The whole table is searched for records that satisfy the conditions of - the where clause. - When the result set is closed, the memory related to its structures and - to a possible temporary table is released. - So its always a good idea to close the result set as soon as its no longer - needed. -\end_layout - -\begin_layout Standard -The following command is supported: -\end_layout - -\begin_layout Itemize - -\family typewriter -SELECT * | column_name [AS output_name] [, ...] | -\begin_inset Newline newline -\end_inset - -function_name(column_name) [AS output_name] [, ...] FROM -\begin_inset Newline newline -\end_inset - -table_name [[AS] table_name_alias] [,...] -\begin_inset Newline newline -\end_inset - -[WHERE condition] [GROUP BY column_name [, ...]] -\begin_inset Newline newline -\end_inset - -[HAVING condition] [ORDER BY column_name [ASC | DESC] [, ...]] -\end_layout - -\begin_deeper -\begin_layout Standard -Description -\end_layout - -\begin_layout Itemize - -\family typewriter -select -\family default - retrieves rows from one or more tables. - The general processing of -\family typewriter -select -\family default - is as follows. -\end_layout - -\begin_layout Standard -Parameters -\end_layout - -\begin_layout Itemize - -\family typewriter -* -\family default - : use this to return all columns in a query. - -\end_layout - -\begin_layout Itemize - -\family typewriter -column_name -\family default -: here you specify the column names to be retrieved, grouped or ordered. -\end_layout - -\begin_layout Itemize - -\family typewriter -function_name -\family default -: the function applied on -\family typewriter -column_name -\family default -. - -\family typewriter -function_name -\family default - must be one of the Litebase functions. -\end_layout - -\begin_layout Itemize - -\family typewriter -table_name -\family default -: the name(s) of the table(s) that will be retrieved. -\end_layout - -\begin_layout Standard -Examples -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -SELECT * FROM person -\end_layout - -\begin_layout Plain Layout - -SELECT * FROM person, department -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard - -\series bold -IMPORTANT OBSERVATIONS -\end_layout - -\begin_layout Itemize - -\family typewriter -SELECT * from table_name -\end_layout - -\begin_deeper -\begin_layout Standard -If a query of this format is issued, with a wild card, a single table, no - -\family typewriter -where -\family default -, -\family typewriter -order by -\family default -, or -\family typewriter -group by -\family default - clauses, the query processing is faster since the table is not analyzed. - Therefore, if the table is altered, the result set will be changed if it - is already opened. - For instance, if this kind of select is issued, not closed and a new row - is inserted, the result set will also return the new row. -\end_layout - -\end_deeper -\begin_layout Itemize -Whenever possible, do not use where clauses that do nothing such as -\family typewriter -1=1 -\family default -. - This increase parser time and the where clause will not be all resolved - using indices, turning the query processing slower. -\end_layout - -\begin_layout Itemize -If instead of a -\family typewriter -* -\family default - the user lists all the fields of table_name in its correct order beginning - with -\family typewriter -rowid -\family default -, the query processing is as faster as when using -\family typewriter -* -\family default -. - However, if an alias is used, it will be ignored in the -\family typewriter -ResultSetMetaData -\family default -. -\end_layout - -\begin_layout Itemize -Queries with no sorting ( -\family typewriter -order by -\family default - and -\family typewriter -group by -\family default -), join and aggregations do not use temporary tables. - If the table is altered and a result set with not temporary table is still - opened, it will be inconsistent. -\end_layout - -\begin_layout Itemize -Condition clause: -\family typewriter -WHERE -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -LIKE -\family default - Statement -\end_layout - -\begin_deeper -\begin_layout Itemize -The char '%' is a wild card in SQL. - It is possible to use '%' at the start, at the end, both, in the middle - (just one), or don't use it. - In the last case, it is the same as equals (=). -\end_layout - -\begin_layout Itemize -Examples: -\family typewriter -WHERE -\family default -... -\end_layout - -\begin_deeper -\begin_layout Standard - -\family typewriter -LIKE '%cacul' -\family default -: it returns the tuples that ends with 'cacul'. -\end_layout - -\begin_layout Standard - -\family typewriter -LIKE 'renato%' -\family default -: it returns the tuples that starts with 'renato'. -\end_layout - -\begin_layout Standard - -\family typewriter -LIKE 'indira%mes' -\family default -: it returns the tuples that starts with 'indira' and ends with 'mes'. -\end_layout - -\begin_layout Standard - -\family typewriter -LIKE 'Zenes' -\family default -: it returns the tuples that is just equal to 'Zenes'. -\end_layout - -\begin_layout Standard - -\family typewriter -LIKE '%Jener%' -\family default -: it returns the tuples that contains 'Jener'. -\end_layout - -\begin_layout Standard - -\family typewriter -LIKE '%Luc%as%' -\family default -: it returns the tuples that contains 'Luc%as'. -\end_layout - -\begin_layout Standard - -\family typewriter -LIKE 'ind%ira%mes' -\family default -: it returns the tuples that starts with 'ind' and ends with 'ira%mes'. - -\end_layout - -\end_deeper -\end_deeper -\end_deeper -\begin_layout Itemize -Order clause: -\family typewriter -ORDER BY -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -asc -\family default - is default. -\end_layout - -\begin_layout Itemize -It is possible to put various columns in the -\family typewriter -order by -\family default - clause. - The -\family typewriter -desc -\family default - expression will work properly if used on any field. - -\end_layout - -\begin_layout Itemize -Examples -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -ORDER BY name, age -\family default -: orders by name and by age. -\end_layout - -\begin_layout Itemize - -\family typewriter -ORDER BY name, age DESC -\family default -: orders by name in ascending order and age in descending order. -\end_layout - -\begin_layout Itemize - -\family typewriter -ORDER BY name DESC, age -\family default -: orders by name in descending order and age in descending order. - -\end_layout - -\end_deeper -\end_deeper -\begin_layout Itemize -Aggregation functions: -\family typewriter -GROUP BY -\end_layout - -\begin_deeper -\begin_layout Itemize -An alias is required for the aggregate function column. -\end_layout - -\begin_layout Itemize -The following aggregation functions are permitted: -\end_layout - -\begin_deeper -\begin_layout Itemize - -\family typewriter -avg() -\end_layout - -\begin_layout Itemize - -\family typewriter -max() -\end_layout - -\begin_layout Itemize - -\family typewriter -min() -\end_layout - -\begin_layout Itemize - -\family typewriter -sum() -\end_layout - -\begin_layout Itemize - -\family typewriter -count(*) -\end_layout - -\end_deeper -\begin_layout Itemize -Example -\end_layout - -\begin_deeper -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ResultSet rs = null; -\end_layout - -\begin_layout Plain Layout - -try -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - rs = driver.executeQuery("select age, sum(region) as tot from person group - by age"); -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -catch(Exception exception) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - Vm.debug(exception.getMessage()); -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -while (rs.next()) -\end_layout - -\begin_layout Plain Layout - - Vm.debug("Name: " + rs.getString("age") + ", Total_Region: " + rs.getString("tot") -); -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Itemize -Note that the alias must be used in the result set. - In the example above, this is -\family typewriter -tot -\family default -. -\end_layout - -\end_deeper -\begin_layout Itemize -A -\family typewriter -ResultSet -\family default - object is returned, as shown in the example above, and can be used to traverse - the rows and also to get the column values. -\end_layout - -\begin_layout Itemize -There's a special column name, -\family typewriter -rowid -\family default -, that can be retrieved or used in the condition. -\end_layout - -\begin_layout Itemize - -\family typewriter -rowid -\family default - cannot be updated. - It is automatically generated for each row in the table, and is unique - for each row, even after row deletions. -\end_layout - -\begin_layout Itemize -If -\family typewriter -rowid -\family default - is used in a condition clause, it is strongly suggested that an index is - created for it. -\end_layout - -\begin_layout Itemize -When using *, the -\family typewriter -rowid -\family default - is not returned! It must be explicitly asked for, followed by the other - columns to be retrieved. -\end_layout - -\begin_layout Itemize -The -\family typewriter -ResultSet -\family default - class contains a very useful method that returns a string matrix with all - fields in the -\family typewriter -ResultSet -\family default -. - It speeds up the filling of -\family typewriter -ListBox -\family default - and -\family typewriter -Grid -\family default -. - All selected columns are transformed to string. -\end_layout - -\begin_layout Itemize -It is possible to use -\family typewriter -setDecimalPlaces() -\family default - to define how the doubles will be formatted when being converted to string. -\end_layout - -\begin_layout Itemize -More Examples: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ResultSet rs = driver.executeQuery("select rowid, name, salary, age from - person where age != 44"); -\end_layout - -\begin_layout Plain Layout - -rs.afterLast(); -\end_layout - -\begin_layout Plain Layout - -while (rs.next()) -\end_layout - -\begin_layout Plain Layout - - Vm.debug("RowID: " + rs.getString(1) + " Name: " + rs.getString(2) + " Salary: - " + rs.getString(3) + " - " + rs.getInt("age")+ " years" ); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// A method that returns a string matrix to fill a ListBox or a Grid. -\end_layout - -\begin_layout Plain Layout - -public String[][] search(char startChar) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - String sql; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // The select statements return only rows where name starts with 'startChar'. -\end_layout - -\begin_layout Plain Layout - - sql = "SELECT rowid, name, gender, married, age, salary FROM person WHERE - name like '" + startChar + "%'"; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - ResultSet rs = driver.executeQuery(sql); -\end_layout - -\begin_layout Plain Layout - - if (rs.first()) -\end_layout - -\begin_layout Plain Layout - - return rs.getStrings(-1,true,false); -\end_layout - -\begin_layout Plain Layout - - else -\end_layout - -\begin_layout Plain Layout - - return null; -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Using the method 'seach'. -\end_layout - -\begin_layout Plain Layout - -private Grid gridNames; -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// The initialization of the Grid must be implemented. - -\end_layout - -\begin_layout Plain Layout - -String[][] people = search("R"); -\end_layout - -\begin_layout Plain Layout - -if (people == null) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - gridNames.clear(); -\end_layout - -\begin_layout Plain Layout - - Vm.debug("There's no person started with this char"); -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -else -\end_layout - -\begin_layout Plain Layout - - gridNames.setItems(people); -\end_layout - -\end_inset - - -\end_layout - -\end_deeper -\begin_layout Subsection - -\family typewriter -prepareStatement() -\end_layout - -\begin_layout Standard - -\family typewriter -public PreparedStatement -\series bold -prepareStatement -\series default -(String sql) -\end_layout - -\begin_layout Standard -Creates a pre-compiled statement with the given SQL. - Prepared statements are faster for repeated queries; instead of parsing - the same query where only a few arguments change, the prepared statement - is created and the query is pre-parsed. - Then, it is only necessary set the arguments (defined as ? in the SQL) - and run the SQL, where the index for the argument starts from 0. - Note that recreating the prepared statement with the same SQL is a waste - of time, which reduces the performance of the program. - Even though the prepared statement is a singleton, trying to prepare the - same SQL many times is a waste of time because there is some processing - and memory usage. -\end_layout - -\begin_layout Standard -It is possible to find a complete example in -\family typewriter -PreparedStatement -\family default - class section. -\end_layout - -\begin_layout Standard -When a table is dropped and recreated, the prepared statements that use - it must be re-prepared because the re-created table might have a different - structure. - This must also be done when the connection is closed and re-opened again. - The explanation for this is similar: the new connection may be different - and Litebase may behave unecpectly if this could be done. -\end_layout - -\begin_layout Subsection - -\family typewriter -getCurrentRowId() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getCurrentRowId -\series default -(String tableName) -\end_layout - -\begin_layout Standard -Returns the current -\family typewriter -rowid -\family default - for a given table. - This method is useful to find the -\family typewriter -rowid -\family default - of the last inserted record: just subtract 1 from the returned value. - After a record is inserted, the -\family typewriter -rowid -\family default - is incremented, so, if one gets the -\family typewriter -rowid -\family default - after inserting the record, it is necessary subtract 1; if one gets the - -\family typewriter -rowid -\family default - before inserting the row, then that's exactly the value. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: the name of the table. -\end_layout - -\begin_layout Standard -Returns: -\end_layout - -\begin_layout Itemize -The current -\family typewriter -rowid -\family default -. -\end_layout - -\begin_layout Standard -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: if the table doesn't exist. -\end_layout - -\begin_layout Standard -\align block -Only note that (although very difficult to happen) the -\family typewriter -rowid -\family default - may be < 0, because it is internally stored in the last 28 bits of an unsigned - integer (32 bits integer), and, thus, if the -\family typewriter -rowid -\family default - gets over 134,217,728, it will be returned as a negative number in this - method. - But this causes no harm. - It can be stored as a negative value that it will be correctly used by - Litebase. - Moreover, it is very unlikely that a table for nowadays devices will get - more then this huge number of inserts. - However, if so, please re-create the table from scratch. -\end_layout - -\begin_layout Subsection - -\family typewriter -getRowCount() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getRowCount -\series default -(String tableName) -\end_layout - -\begin_layout Standard -Returns the number of valid rows in a table. - This may be different from the number of records if a row has been deleted. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name to get number of rows. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The number of rows. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -See -\begin_inset space ~ -\end_inset - -Also: -\end_layout - -\begin_layout Itemize - -\family typewriter -getRowCountDeleted() -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -setRowInc() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setRowInc -\series default -(String tableName, int inc) -\end_layout - -\begin_layout Standard -Sets the row increment used when creating or updating big amounts of data. -\end_layout - -\begin_layout Standard -Using this method greatly increases the speed of bulk insertions (about - 3x faster). - To use it, you must call it (preferable) with the amount of rows that will - be inserted. - -\end_layout - -\begin_layout Standard -After the insertion is finished, it is NECESSARY to call it again, passing - -1 as the inc argument. - Without doing this last step, one may lose data because some writes will - be delayed until the method is called again with -1. - Another good optimization on bulk insertions is to drop the indices and - then create them afterwards. - So, to correctly use -\family typewriter -setRowInc() -\family default -, one must: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -driver.setRowInc("table_name", totalNumberOfRows); -\end_layout - -\begin_layout Plain Layout - -... -\end_layout - -\begin_layout Plain Layout - -// Fetches the data and insert them. -\end_layout - -\begin_layout Plain Layout - -... -\end_layout - -\begin_layout Plain Layout - -driver.setRowInc("table_name", -1); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -Using prepared statements on insertion makes it another couple of times - faster. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name. - -\end_layout - -\begin_layout Itemize - -\family typewriter -inc -\family default -: The row increment. -\end_layout - -\begin_layout Subsection - -\family typewriter -convert() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -convert -\series default -(String tableName) -\end_layout - -\begin_layout Standard -Converts a table from the previous Litebase table version to the current - one. - If the table format is older than the previous table version, this method - can't be used and a -\family typewriter -DriverException -\family default - will be thrown. - It is possible to know if the table version is not compatible with the - current version used in Litebase because an exception will be thrown if - one tries to open a table with the old format. - The table name to be converted must be specified. - The table will be closed after using this method and must be closed before - it. - Notice that the table -\family typewriter -.db -\family default - file will be overwritten, so it is highly recommended to backup the tables. - Nowadays, the -\family typewriter -convert() -\family default - works to convert from versions 2.1 till 2.14 to the current version. - Tables from the versions 2.20 to 2.52 do not need to be converted to the - table format for Litebase 2.53. - -\end_layout - -\begin_layout Standard -For more information of how to use this, see the chapter -\begin_inset CommandInset ref -LatexCommand nameref -reference "chap:Migration-from-Different" - -\end_inset - -. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name to convert. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the table format is older than the previous table version. -\end_layout - -\begin_layout Subsection - -\family typewriter -exists() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -exists -\series default -(String tableName) -\end_layout - -\begin_layout Standard -Returns if the given table already exists. - This method can be used before dropping the table. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name to check if it exists. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the table exists; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default - If the -\family typewriter -.db -\family default - file exists but not the -\family typewriter -.dbo -\family default - file. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -if (driver.exists("person") -\end_layout - -\begin_layout Plain Layout - - driver.executeUpdate("DROP TABLE person"); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -closeAll() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -closeAll -\series default -() -\end_layout - -\begin_layout Standard -Closes all files, so that they are immediately flushed to disk (on the desktop) - and releases the file handles (on the device). - When this method is issued, all tables opened in the connection are also - closed and the prepared statements will be in an invalid state. - Therefore, the prepared statement of the closed connection must be prepared - again if they will be used later on. - Note that, after this is called, all result sets and prepared statements - created with this Litebase instance will be in an inconsistent state, and - using them will raise an -\family typewriter -IllegalStateException -\family default -. - This method also deletes the active instance for this creator id from Litebase - internal table. - -\end_layout - -\begin_layout Subsection - -\family typewriter -purge() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -purge -\series default -(String tableName) -\end_layout - -\begin_layout Standard -Used to delete physically the records of the given table. - Records are always deleted logically, to avoid the need of recreating the - indices. - When a new record is added, it doesn't occupy the position of the previously - deleted one. - This can make the table big, if a table is created, filled and have a couple - of records deleted, thus wasting space. - This method will remove all deleted records and recreate the indices accordingl -y. - Note that it can take some time to run. - Therefore this operation should not be executed very often, This should - not be done if your table has less then 10% of deleted rows. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Important: -\end_layout - -\begin_layout Itemize -The -\family typewriter -rowid -\family default - of the records is NOT changed with this operation. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name to purge. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The number of purged records. -\end_layout - -\begin_layout Subsection - -\family typewriter -getRowCountDeleted() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getRowCountDeleted -\series default -(String tableName) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The number of deleted records. -\end_layout - -\begin_layout Subsection - -\family typewriter -getRowIterator() -\begin_inset CommandInset label -LatexCommand label -name "sub:getRowIterator()" - -\end_inset - - -\end_layout - -\begin_layout Standard - -\family typewriter -public RowIterator -\series bold -getRowIterator -\series default -(String tableName) -\end_layout - -\begin_layout Standard -With it it is possible to iterate through all the rows of a table in sequence - and get its attributes. - This is good for synchronizing a table. - While the iterator is active, no queries or updates must be done, unless - one wants to corrupt the data. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name to get a row iterator. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -RowIterator -\family default - object. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// This method removes physically all deleted records on table person and - sets all records as sync. -\end_layout - -\begin_layout Plain Layout - -public void setTableSync() -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - RowIterator ri = null; -\end_layout - -\begin_layout Plain Layout - - try -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - driver.purge("person"); // Deletes physically the rows. - -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // Sets all as synchronized. - -\end_layout - -\begin_layout Plain Layout - - ri = driver.getRowIterator("person"); -\end_layout - -\begin_layout Plain Layout - - while (ri.next()) -\end_layout - -\begin_layout Plain Layout - - ri.setSynced(); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - catch (Exception exception) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - -\backslash -* ... - * -\backslash - -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - finally -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - if (ri != null) -\end_layout - -\begin_layout Plain Layout - - ri.close(); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -getLogger() -\end_layout - -\begin_layout Standard - -\family typewriter -public static synchronized Logger -\series bold -getLogger -\series default -() -\end_layout - -\begin_layout Standard - -\family typewriter -LitebaseConnection.logger -\family default - is public and can be accessed directly. - This should be done unless logger is used within threads. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The current Litebase logger, a -\family typewriter -Logger -\family default - object. - -\end_layout - -\begin_layout Subsection - -\family typewriter -setLogger() -\end_layout - -\begin_layout Standard - -\family typewriter -public static synchronized void -\series bold -setLogger -\series default -(Logger logger) -\end_layout - -\begin_layout Standard -Sets the litebase logger. - This enables log messages for all queries and statements of Litebase and - can be very useful to help finding bugs in the system. - Logs take up memory space, so turn them on only when necessary. - -\end_layout - -\begin_layout Standard - -\family typewriter -LitebaseConnection.logger -\family default - is public and can be accessed directly. - This should be done unless logger is used within threads. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -logger -\family default -: The -\family typewriter -Logger -\family default - object to be used with Litebase. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getDefaultLogger() -\end_layout - -\begin_layout Standard - -\family typewriter -public static synchronized Logger -\series bold -getDefaultLogger -\series default -() -\end_layout - -\begin_layout Standard -Gets the default Litebase logger. - This was already explained. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A logger for Litebase, which is a -\family typewriter -Logger -\family default - object. - -\end_layout - -\begin_layout Subsection - -\family typewriter -DeleteLogFiles() -\end_layout - -\begin_layout Standard - -\family typewriter -public static int -\series bold -deleteLogFiles -\series default -() -\end_layout - -\begin_layout Standard -Deletes all log files found in the device. - If log is enabled, the current log file is not affected by this command. - It only deletes logs created with -\family typewriter -getDefaultLogger(). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The number of files deleted. -\end_layout - -\begin_layout Subsection - -\family typewriter -processLogs() -\end_layout - -\begin_layout Standard - -\family typewriter -public static LitebaseConnection -\series bold -processLogs -\series default -(String[] sql, String params, -\begin_inset Newline newline -\end_inset - -boolean debug) -\end_layout - -\begin_layout Standard -This is a handy method that can be used to reproduce all commands of a log - file. - This is intended to be used by the development team only. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -sql -\family default -: An array of SQL commands. -\end_layout - -\begin_layout Itemize - -\family typewriter -params -\family default -: Similar to params from -\family typewriter -getInstance() -\family default - -\begin_inset CommandInset ref -LatexCommand vpageref -reference "sub:getInstance()" - -\end_inset - - -\end_layout - -\begin_layout Itemize - -\family typewriter -debug -\family default -: If -\family typewriter -true -\family default -, debugs the commands and possible exceptions to the debug console. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -LitebaseConnection -\family default - instance created, or -\family typewriter -null -\family default - if -\family typewriter -closeAll() -\family default - was the last command executed (or no commands were executed at all). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -String[] sql = -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - "new LitebaseConnection(MBSL,null)", -\end_layout - -\begin_layout Plain Layout - - "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO - int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20), DESCRICAO char(100), UNDCAIXA - char(10), PESO float, UNIDADEMEDIDA char(3), EMBALAGEM char(10), PORCTROCA - float, PERMITETROCA int)", -\end_layout - -\begin_layout Plain Layout - - "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)", -\end_layout - -\begin_layout Plain Layout - - "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)", -\end_layout - -\begin_layout Plain Layout - - "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)", -\end_layout - -\begin_layout Plain Layout - - "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)", -\end_layout - -\begin_layout Plain Layout - - "closeAll", -\end_layout - -\begin_layout Plain Layout - - "new LitebaseConnection(MBSL, null)", -\end_layout - -\begin_layout Plain Layout - - "insert into PRODUTO values(1, '19132', 2, 1, '1, 2, 3', 'ABSORVENTE SILHO - ABAS','5',13,'PCT','20X30',10,0)" -\end_layout - -\begin_layout Plain Layout - -}; -\end_layout - -\begin_layout Plain Layout - -LitebaseConnection.processLogs(sql, true); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -Finally, for this method, the path can't be passed in new -\family typewriter -LitebaseConnection -\family default -; -\family typewriter -params -\family default - must be used instead. - It is not forbidden to pass a path to the connection, but it is not used. -\end_layout - -\begin_layout Subsection - -\family typewriter -recoverTable() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -recoverTable -\series default -(String tableName) -\end_layout - -\begin_layout Standard -If a table is not closed properly and was modified since the last opening, - that is, closed when exiting the application properly or issuing a -\family typewriter -closeAll() -\family default -, when trying to open it again, a -\family typewriter - -\begin_inset Newline newline -\end_inset - -TableNotClosedException -\family default - will be thrown. - This does not mean that the table is corrupted, but it can be the case. -\end_layout - -\begin_layout Standard -Therefore, whenever a table cannot be opened because of the exception above, - -\family typewriter - -\begin_inset Newline newline -\end_inset - -recoverTable() -\family default - should be used to recover it. - Notice that the table must be closed before using it, which will be the - case if a -\family typewriter -TableNotClosedException -\family default - is thrown. - It will also be closed after this method finishes. -\end_layout - -\begin_layout Standard -This method will invalidate every record whose CRC does not match the stored - CRC, marking it as deleted and settings its -\family typewriter -rowid -\family default - to zero. - The corrupted records can be accessed via -\family typewriter -RowIterator -\family default - methods. - If the table was closed properly before calling this method, a -\family typewriter -DriverException -\family default - will be thrown because there is nothing to be recovered. - -\end_layout - -\begin_layout Standard -Many programmers try to recover the tables in the beginning of the application. - If there is something to be recovered, it can be a slow process and the - application user might think that the application has frozen and reset - the device. - To avoid this, it is a good idea to tell the user what is happening. - A suggestion is given below, using the methods -\family typewriter -listAllTables() -\family default - and -\family typewriter -isTableProperlyClosed() -\family default -. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The name of the table to be recovered. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if there was rows to be recovered; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the table was closed properly before calling this method. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// Searches for a not closed properly table and recovers it. -\end_layout - -\begin_layout Plain Layout - -LitebaseConnection con = LitebaseConnection.getInstance(); -\end_layout - -\begin_layout Plain Layout - -String[] arqs = con.listAllTables(); -\end_layout - -\begin_layout Plain Layout - -int notClosed = 0, -\end_layout - -\begin_layout Plain Layout - - i = args.length, -\end_layout - -\begin_layout Plain Layout - - j = 1; -\end_layout - -\begin_layout Plain Layout - -while (--i >= 0) -\end_layout - -\begin_layout Plain Layout - - if (!con.isTableProperlyClosed(arqs[i])) -\end_layout - -\begin_layout Plain Layout - - notClosed++; -\end_layout - -\begin_layout Plain Layout - -if (notClosed > 0) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - String msg1 = "Recovering data -\backslash -n(step ", -\end_layout - -\begin_layout Plain Layout - - msg2 = " out of " + notClosed + ")"; -\end_layout - -\begin_layout Plain Layout - - ProgressBox pb = new ProgressBox("", msg1 + '0' + msg2, null); -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - pb.popupNonBlocking(); -\end_layout - -\begin_layout Plain Layout - - i = args.length; -\end_layout - -\begin_layout Plain Layout - - while (--i >= 0) -\end_layout - -\begin_layout Plain Layout - - if (!con.isTableProperlyClosed(arqs[i])) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - try -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - con.recoverTable(arqs[i]); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - catch (Exception exception) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - exception.printStackTrace(); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - pb.setText(msg1 + (j++) + msg2); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - pb.unpop(); -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -getSlot() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getSlot -\series default -() -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -Deprecated . - It was the slot being used on Palm OS. - It just returns -1. -\end_layout - -\begin_layout Subsection - -\family typewriter -isOpen() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isOpen -\series default -(String tableName) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The name of the table to check if it is open.. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the table is opened in the current connection; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -dropDatabase() -\end_layout - -\begin_layout Standard - -\family typewriter -public static void -\series bold -dropDatabase -\series default -(String crid, String sourcePath, int slot) -\family default - -\end_layout - -\begin_layout Standard -Drops all the tables from a database represented by its application id and - path. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -crid -\family default -: The application id of the database. -\end_layout - -\begin_layout Itemize - -\family typewriter -sourcePath -\family default -: The path where the files are stored. -\end_layout - -\begin_layout Itemize - -\family typewriter -slot -\family default -: It was the slot on Palm OS where the source path folder was stored. - Not used anymore. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the database is not found. -\end_layout - -\begin_layout Subsection - -\family typewriter -isTableProperlyClosed() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isTableProperlyClosed -\series default -(String tableName) -\end_layout - -\begin_layout Standard -Indicates if a table is closed properly or not. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table to be verified. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the table is closed properly or is open (a not properly closed table - can't be opened); -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the table is corrupted. - -\end_layout - -\begin_layout Subsection - -\family typewriter -listAllTables() -\end_layout - -\begin_layout Standard - -\family typewriter -public String[] -\series bold -listAllTables -\series default -() -\end_layout - -\begin_layout Standard -Lists all the table names from the current connection. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -An array of all the table names of the current connection. -\end_layout - -\begin_layout Section - -\family typewriter -ResultSet -\family default - Class -\end_layout - -\begin_layout Standard -This class represents a set or rows resulting from a -\family typewriter -LitebaseConnection.executeQuery() -\family default - method call. - It cannot be directly instantiated. - With this class you can cover the virtual rows of table, and you can discover - the meta data of a table. - These methods will also throw an -\family typewriter - -\begin_inset Newline newline -\end_inset - -IllegalStateException -\family default - if the result set or the Litebase connection where it was created is closed. - I -\backslash -O problems will also raise -\family typewriter -DriverException -\family default -s. - When accessing the result set columns, invalid indices will raise an -\family typewriter -IllegalArgumentException -\family default -, unknown column names will raise a -\family typewriter -DriverException -\family default -, and trying to fetch a column value from the result set with a method to - fetch data with a incompatible type will result in -\family typewriter -DriverException -\family default -. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getResultSetMetaData() -\end_layout - -\begin_layout Standard - -\family typewriter -public ResultSetMetaData -\series bold -getResultSetMetaData -\series default -() -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The meta data for this result set. - -\end_layout - -\begin_layout Standard -See the -\family typewriter -ResultSetMetaData -\family default - class -\begin_inset CommandInset ref -LatexCommand vpageref -reference "sec:ResultSetMetaData-Class" - -\end_inset - -for more information. - There it can be found a good example with -\family typewriter -ResultSet -\family default - and the -\family typewriter -ResultSetMetaData -\family default - classes. -\end_layout - -\begin_layout Subsection - -\family typewriter -close() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -close -\series default -() -\end_layout - -\begin_layout Standard -Releases all memory allocated for this object. - Its a GOOD idea to call this when it is no longer needed, but it is also - called by the GC when the object is no longer in use. - This can be issued even if the driver is already closed. -\end_layout - -\begin_layout Subsection - -\family typewriter -beforeFirst() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -beforeFirst -\series default -() -\end_layout - -\begin_layout Standard -Places the cursor before the first record. -\end_layout - -\begin_layout Subsection - -\family typewriter -afterLast() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -afterLast -\series default -() -\end_layout - -\begin_layout Standard -Places the cursor after the last record. -\end_layout - -\begin_layout Subsection - -\family typewriter -first() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -first -\series default -() -\end_layout - -\begin_layout Standard -Places the cursor in the first record. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if it was possible to place the cursor in the first record; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -last() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -last -\series default -() -\end_layout - -\begin_layout Standard -Places the cursor in the last record. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if it was possible to place the cursor in the last record; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -next() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -next -\series default -() -\end_layout - -\begin_layout Standard -Places the cursor in the next record of this -\family typewriter -ResultSet -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if it was possible to place the cursor in the next record; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -prev() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -prev -\series default -() -\end_layout - -\begin_layout Standard -Places the cursor in the record of this -\family typewriter -ResultSet -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if it was possible to place the cursor in the previous record; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -getShort() -\end_layout - -\begin_layout Standard - -\family typewriter -public short -\series bold -getShort -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -short -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -short -\family default - if it was created with this precision or if the data being fetched is the - result of a -\family typewriter -date -\family default - or -\family typewriter -datetime -\family default - SQL function. -\end_layout - -\begin_layout Subsection - -\family typewriter -getShort() -\end_layout - -\begin_layout Standard - -\family typewriter -public short -\series bold -getShort -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a short value that is - represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -short -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getInt() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getInt -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -An -\family typewriter -int -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -int -\family default - if it was created with this precision. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getInt() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getInt -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns an -\family typewriter -int -\family default - value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -An -\family typewriter -int -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getLong() -\end_layout - -\begin_layout Standard - -\family typewriter -public long -\series bold -getLong -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -long -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -long -\family default - if it was created with this precision. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getLong() -\end_layout - -\begin_layout Standard - -\family typewriter -public long -\series bold -getLong -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -long -\family default - value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -long -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getFloat() -\end_layout - -\begin_layout Standard - -\family typewriter -public float -\series bold -getFloat -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -float -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0.0. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -float -\family default - if it was created with this precision. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getFloat() -\end_layout - -\begin_layout Standard - -\family typewriter -public float -\series bold -getFloat -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -float -\family default - value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -float -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0.0. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDouble() -\end_layout - -\begin_layout Standard - -\family typewriter -public double -\series bold -getDouble -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -double -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0.0. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -double -\family default - if it was created with this precision. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDouble() -\end_layout - -\begin_layout Standard - -\family typewriter -public double -\series bold -getDouble -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -double -\family default - value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -double -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is 0.0. -\end_layout - -\begin_layout Subsection - -\family typewriter -getChars() -\end_layout - -\begin_layout Standard - -\family typewriter -public char[] -\series bold -getChars -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -char -\family default - array that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -char[] -\family default - if it was created as a string type ( -\family typewriter -char -\family default -, -\family typewriter -char nocase -\family default -, -\family typewriter -varchar -\family default -, or -\family typewriter -varchar nocase -\family default -). -\end_layout - -\begin_layout Subsection - -\family typewriter -getChars() -\end_layout - -\begin_layout Standard - -\family typewriter -public char[] -\series bold -getChars -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -char -\family default - array value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -char -\family default - array that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getBlob() -\end_layout - -\begin_layout Standard - -\family typewriter -public byte[] -\series bold -getBlob -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -byte -\family default - array that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. - -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -byte[] -\family default - if it was created as a -\family typewriter -blob -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getBlob() -\end_layout - -\begin_layout Standard - -\family typewriter -public byte[] -\series bold -getBlob -\series default -( -\series bold -String -\series default - colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -byte -\family default - array value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -byte -\family default - array that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getString() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getString -\series default -(int colIdx) -\end_layout - -\begin_layout Standard -Any column type can be returned as a string except for -\family typewriter -blob -\family default -. - -\family typewriter -double -\family default -/ -\family typewriter -float -\family default - values formatting will use the precision set with the -\family typewriter -setDecimalPlaces() -\family default - method. - If the field has -\family typewriter -datetime -\family default - type, the millis is not returned. - One can use the -\family typewriter -getTime() -\family default - to get the millis. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The column value as a string. - If the value is SQL -\family typewriter -null -\family default - or the column is of type blob, the value returned is -\family typewriter -null -\family default -. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getString() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getString -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a string that is represented - by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The column value as a string. - If the value is SQL -\family typewriter -null -\family default - or the column is of type -\family typewriter -blob -\family default -, the value returned is -\family typewriter -null -\family default -. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getStrings() -\end_layout - -\begin_layout Standard - -\family typewriter -public String[][] -\series bold -getStrings -\series default -() -\end_layout - -\begin_layout Standard -Starting from the current cursor position (must use -\family typewriter -first() -\family default -, -\family typewriter -last() -\family default -, -\family typewriter -prev() -\family default - or -\family typewriter -next() -\family default -, not -\family typewriter -beforeFirst() -\family default - or -\family typewriter -afterLast() -\family default -), it reads all result set rows that's being requested from the current - one. - This method moves the cursor to the row after the last one fetched. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A matrix, where -\family typewriter -String[0] -\family default - is the first row, and -\family typewriter -String[0][0], String[0][1]... - -\family default - are the column elements of the first row. - Returns -\family typewriter -null -\family default - if there's no more element to be fetched. - -\family typewriter -double -\family default -/ -\family typewriter -float -\family default - values will be formatted using the -\family typewriter -setDecimalPlaces() -\family default - settings. - If the value is SQL -\family typewriter -null -\family default -, the value returned in its matrix position is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: if the result set cursor is in an invalid position. -\end_layout - -\begin_layout Subsection - -\family typewriter -getStrings() -\end_layout - -\begin_layout Standard - -\family typewriter -public String[][] -\series bold -getStrings -\series default -(int count) -\end_layout - -\begin_layout Standard -This is the most powerful method of this class. - Starting from the current cursor position (must use -\family typewriter -first() -\family default -, -\family typewriter -last() -\family default -, -\family typewriter -prev() -\family default - or -\family typewriter -next() -\family default -, not -\family typewriter -beforeFirst() -\family default - or -\family typewriter -afterLast() -\family default -), it reads the smallest number of rows between the requested amount and - the number of rows from the current cursor position till the end of the - result set. - This method moves the cursor to the row after the last one fetched. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -count: -\family default - The number of rows to be fetched, or -1 for all. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A matrix, where -\family typewriter -String[0] -\family default - is the first row, and -\family typewriter -String[0][0], String[0][1]... - -\family default - are the column elements of the first row. - Returns -\family typewriter -null -\family default - if there's no more element to be fetched. - -\family typewriter -double -\family default -/ -\family typewriter -float -\family default - values will be formatted using the -\family typewriter -setDecimalPlaces() -\family default - settings. - If the value is SQL -\family typewriter -null -\family default -, the value returned in its matrix position is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: if the result set pointer is in an invalid position. -\end_layout - -\begin_layout Itemize - -\family typewriter -IllegalArgumentException -\family default -: if the count argument is smaller than -1. -\end_layout - -\begin_layout Standard -NOTE: To populate a grid, since the application won't be able to show all - the rows at the same time if there are many of them, it is better to use - this version of -\family typewriter -getStrings() -\family default - instead of loading all the result set in the grid using the previous method. - This way, the grid is being filled in runtime when the user rolls the scroll - bar. - Moreover, this reduces the chances of getting an -\family typewriter -OutOfMemoryError -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -import litebase.*; -\end_layout - -\begin_layout Plain Layout - -import totalcross.ui.*; -\end_layout - -\begin_layout Plain Layout - -import totalcross.ui.dialog.*; -\end_layout - -\begin_layout Plain Layout - -import totalcross.ui.event.*; -\end_layout - -\begin_layout Plain Layout - -import totalcross.util.Comparable; -\end_layout - -\begin_layout Plain Layout - -import totalcross.sys.*; -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -public class TCTestWin extends MainWindow implements PressListener, Grid.DataSour -ce -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - static class CodDesc implements Comparable -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - String desc; -\end_layout - -\begin_layout Plain Layout - - int index; // The table is ordered by codigo. -\end_layout - -\begin_layout Plain Layout - - static ResultSet rs; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - public CodDesc(int idx) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - index = idx; -\end_layout - -\begin_layout Plain Layout - - desc = rs.getString("descricao"); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - public int compareTo(Object other) throws ClassCastException -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - return desc.compareTo(((CodDesc)other).desc); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - LitebaseConnection lb = LitebaseConnection.getInstance(); -\end_layout - -\begin_layout Plain Layout - - Grid grid; -\end_layout - -\begin_layout Plain Layout - - RadioGroupController rg; -\end_layout - -\begin_layout Plain Layout - - CodDesc[] cds; // Ordered by codigo. -\end_layout - -\begin_layout Plain Layout - - CodDesc[] dcs; // Ordered by descricao. -\end_layout - -\begin_layout Plain Layout - - Label stat; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - public TCTestWin() -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - setUIStyle(Settings.Vista); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - public void initUI() -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - try -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - stat = new Label("Loading..."); -\end_layout - -\begin_layout Plain Layout - - stat.setFont(font.asBold()); -\end_layout - -\begin_layout Plain Layout - - add(stat, LEFT, AFTER, FILL, PREFERRED); -\end_layout - -\begin_layout Plain Layout - - repaintNow(); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - // First, loads. -\end_layout - -\begin_layout Plain Layout - - int ini = Vm.getTimeStamp(); -\end_layout - -\begin_layout Plain Layout - - ResultSet rs = lb.executeQuery("select rowid, codigo, codigogrupo, descricao, - embalagem, vendaminima, preco, codigobarras, estoque, obs, flaggrade, falta, - novo, promocao, descontocmaximo from tblafvproduto01"); -\end_layout - -\begin_layout Plain Layout - - cds = new CodDesc[rs.getRowCount()]; -\end_layout - -\begin_layout Plain Layout - - CodDesc.rs = rs; -\end_layout - -\begin_layout Plain Layout - - for (int i = 0; rs.next(); i++) -\end_layout - -\begin_layout Plain Layout - - cds[i] = new CodDesc(i); -\end_layout - -\begin_layout Plain Layout - - stat.setText("Load time: " + (Vm.getTimeStamp() - ini) + "ms"); -\end_layout - -\begin_layout Plain Layout - - stat.repaintNow(); -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // Now, sorts. -\end_layout - -\begin_layout Plain Layout - - ini = Vm.getTimeStamp(); -\end_layout - -\begin_layout Plain Layout - - dcs = new CodDesc[cds.length]; -\end_layout - -\begin_layout Plain Layout - - Vm.arrayCopy(cds, 0, dcs, 0, cds.length); // Copies the references. -\end_layout - -\begin_layout Plain Layout - - qsortDesc(dcs, 0, dcs.length - 1); -\end_layout - -\begin_layout Plain Layout - - stat.setText(stat.getText() + " . - Sorting: " -\end_layout - -\begin_layout Plain Layout - - + (Vm.getTimeStamp() - ini) + "ms"); -\end_layout - -\begin_layout Plain Layout - - String[] tit {"rowid", "codigo", "codigogrupo", "descricao", "embalagem", - "vendaminima", "preco", "codigobarras", "estoque", "obs", "flaggrade", - "falta", "novo", "promocao", "descontocmaximo"}; -\end_layout - -\begin_layout Plain Layout - - int[] align = {LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, - LEFT, LEFT, LEFT, LEFT, LEFT, LEFT}; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // Hides rowid and codigogrupo. - Negative values mean %. -\end_layout - -\begin_layout Plain Layout - - int[] widths = {0, -10, 0, -30, -10, -10, -10, -10, -10, -10, -10, -10, - -10, -10, -10}; -\end_layout - -\begin_layout Plain Layout - - Radio r = new Radio("Cdigo", rg = new RadioGroupController()); -\end_layout - -\begin_layout Plain Layout - - add(new Label("Order by"), LEFT, AFTER, PREFERRED, r.getPreferredHeight()); -\end_layout - -\begin_layout Plain Layout - - add(r, AFTER + 2, SAME); r.addPressListener(this); -\end_layout - -\begin_layout Plain Layout - - r.setChecked(true); -\end_layout - -\begin_layout Plain Layout - - add(r = new Radio("Descrio", rg), AFTER + 2, SAME); -\end_layout - -\begin_layout Plain Layout - - r.addPressListener(this); -\end_layout - -\begin_layout Plain Layout - - Grid.useHorizontalScrollBar = true; -\end_layout - -\begin_layout Plain Layout - - add(grid = new Grid(tit, widths, align, false), LEFT, AFTER + 2, FILL, - FILL); -\end_layout - -\begin_layout Plain Layout - - Grid.useHorizontalScrollBar = false; -\end_layout - -\begin_layout Plain Layout - - grid.liveScrolling = true; -\end_layout - -\begin_layout Plain Layout - - grid.setDataSource(this, cds.length); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - catch (Exception exception) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - MessageBox.showException(exception, false); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - public void controlPressed(ControlEvent event) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - grid.fetchDataSource(); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - public String[][] getItems(int startingRow, int count) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - ResultSet rs = CodDesc.rs; -\end_layout - -\begin_layout Plain Layout - - CodDesc[] qual = rg.getSelectedIndex() == 0? cds : dcs; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // Initialies the array of strings that will store each row. -\end_layout - -\begin_layout Plain Layout - - String[][] v = new String[count][]; -\end_layout - -\begin_layout Plain Layout - - for (int i = 0; i < count; i++) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - CodDesc cd = qual[startingRow++]; -\end_layout - -\begin_layout Plain Layout - - rs.absolute(cd.index); -\end_layout - -\begin_layout Plain Layout - - v[i] = rs.getStrings(1)[0]; -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - return v; -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - static void qsortDesc(CodDesc[] items, int first, int last) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - if (first >= last) -\end_layout - -\begin_layout Plain Layout - - return; -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - int low = first; -\end_layout - -\begin_layout Plain Layout - - int high = last; -\end_layout - -\begin_layout Plain Layout - - CodDesc mid = items[(first + last) >> 1]; -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - - while (true) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - String s = mid.desc; -\end_layout - -\begin_layout Plain Layout - - while (high >= low && s.compareTo(items[low].desc) > 0) -\end_layout - -\begin_layout Plain Layout - - low++; -\end_layout - -\begin_layout Plain Layout - - while (high >= low && s.compareTo(items[high].desc) < 0) -\end_layout - -\begin_layout Plain Layout - - high--; -\end_layout - -\begin_layout Plain Layout - - if (low <= high) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - CodDesc temp = items[low]; -\end_layout - -\begin_layout Plain Layout - - items[low++] = items[high]; -\end_layout - -\begin_layout Plain Layout - - items[high--] = temp; -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - else -\end_layout - -\begin_layout Plain Layout - - break; -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - if (first < high) -\end_layout - -\begin_layout Plain Layout - - qsortDesc(items, first, high); -\end_layout - -\begin_layout Plain Layout - - if (low < last) -\end_layout - -\begin_layout Plain Layout - - qsortDesc(items, low, last); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -getDate() -\end_layout - -\begin_layout Standard - -\family typewriter -public Date -\series bold -getDate -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -Date -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -date -\family default - if it was created with -\family typewriter -date -\family default - data type. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDate() -\end_layout - -\begin_layout Standard - -\family typewriter -public Date -\series bold -getDate -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -Date -\family default - value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -Date -\family default - value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDateTime() -\end_layout - -\begin_layout Standard - -\family typewriter -public Time -\series bold -getDateTime -\series default -(int colIdx) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -Time -\family default - (corresponding to the -\family typewriter -datetime -\family default - Litebase data type) value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Standard -Note that it is only possible to request this column as -\family typewriter -datetime -\family default - if it was created with -\family typewriter -datetime -\family default - data type. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDateTime() -\end_layout - -\begin_layout Standard - -\family typewriter -public Time -\series bold -getDateTime -\series default -(String colName) -\end_layout - -\begin_layout Standard -Given the column name (case insensitive), returns a -\family typewriter -Time -\family default - value that is represented by this column. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -A -\family typewriter -Time -\family default - (corresponding to the -\family typewriter -datetime -\family default - Litebase data type) value that is represented by this column. - If the value is SQL -\family typewriter -null -\family default -, the value returned is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -absolute() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -absolute -\series default -(int row) -\end_layout - -\begin_layout Standard -Places this result set cursor at the given absolute row (starting from 0). - This is the absolute physical row of the result set table. - This method is usually used to restore the row at a previous row returned - with the -\family typewriter -getRow() -\family default - method. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -row -\family default -: The row number of where the cursor will be placed. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default -: If no error occurs (no exception is thrown). - -\end_layout - -\begin_layout Subsection - -\family typewriter -relative() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -relative -\series default -(int rows) -\end_layout - -\begin_layout Standard -Moves the cursor -\family typewriter -rows -\family default - in distance. - The value can be greater or lower than zero. - It searches the position until finding the right row or the end or the - beginning of the result set table. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -row -\family default -s: The distance to move the cursor. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default -: If no error occurs (no exception is thrown). - -\end_layout - -\begin_layout Subsection - -\family typewriter -getRow() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getRow -\series default -() -\end_layout - -\begin_layout Standard -Returns the current physical row of the table where the cursor is (starting - from 0). - Must be used with the absolute method. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default -: Returns the current physical row of the table where the cursor is (starting - from 0). - -\end_layout - -\begin_layout Subsection - -\family typewriter -setDecimalPlaces() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setDecimalPlaces -\series default -(int colIdx, int places) -\end_layout - -\begin_layout Standard -Sets the number of decimal places that the given column (starting from 1) - will have when being converted to string. - Must be used for columns of type -\family typewriter -double -\family default - or -\family typewriter -float -\family default - only. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index. -\end_layout - -\begin_layout Itemize - -\family typewriter -places -\family default -: The number of decimal places. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the value for decimal places is invalid ( -\family typewriter -< -1 -\family default - or -\family typewriter -> 40 -\family default -) or the column is not of type -\family typewriter -float -\family default - or -\family typewriter -double -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getRowCount() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getRowCount -\series default -() -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The number of rows of this result set. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -ResultSet rs = null; -\end_layout - -\begin_layout Plain Layout - -try -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - rs = driver.executeQuery("select salary, age from person"); -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -catch (Exception exception) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - /*...*/ -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Sets decimal places for salary that is double. -\end_layout - -\begin_layout Plain Layout - -rs.setDecimalPlaces(1, 5); -\end_layout - -\begin_layout Plain Layout - -while (rs.next()) -\end_layout - -\begin_layout Plain Layout - - Vm.debug("salary: " + rs.getString("salary") + ", age: " + rs.getShort("age")); - -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Places in the first rows. -\end_layout - -\begin_layout Plain Layout - -rs.first(); -\end_layout - -\begin_layout Plain Layout - -Vm.debug("Row: " + rs.getRow() + " salary: " + rs.getString("salary") -\end_layout - -\begin_layout Plain Layout - -+ ", age: " + rs.getShort("age")); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Moves the cursor rows in distance relative a current position. - -\end_layout - -\begin_layout Plain Layout - -rs.relative(rs.getRowCount() - 1); -\end_layout - -\begin_layout Plain Layout - -Vm.debug("Row: " + rs.getRow() + " salary: " + rs.getString("salary") -\end_layout - -\begin_layout Plain Layout - -+ ", age: " + rs.getShort("age")); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Places the cursor in an absolute position. -\end_layout - -\begin_layout Plain Layout - -rs.absolute(2); -\end_layout - -\begin_layout Plain Layout - -Vm.debug("Row: " + rs.getRow() + " salary: "+rs.getString("salary") -\end_layout - -\begin_layout Plain Layout - -+ ", age: " + rs.getShort("age")); -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -rs.close(); // Closes the resultset. -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -isNull() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isNull -\series default -(int colIdx) -\end_layout - -\begin_layout Standard -Indicates if this column has a -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colIdx -\family default -: The column index (starting from 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -Retuns: -\family typewriter -true -\family default - if this column has a null; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -isNull() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isNull -\series default -(int colName) -\end_layout - -\begin_layout Standard -Indicates if this column has a -\family typewriter -null -\family default -. - It behaves similar to the previous one. - However, it is slower. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -colName -\family default -: The column name. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -Retuns: -\family typewriter -true -\family default - if this column has a null; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -rowToString() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -rowToString -\series default -() -\end_layout - -\begin_layout Standard -Returns a whole current row of a -\family typewriter -ResultSet -\family default - in a string with column data separated by tab. - With a column has a -\family typewriter -null -\family default - or empty value, the string will have two consecutive tabs ( -\begin_inset Quotes sld -\end_inset - - -\family typewriter - -\backslash -t -\backslash -t -\family default - -\begin_inset Quotes srd -\end_inset - -). - Blobs are treated as nulls. -\end_layout - -\begin_layout Section - -\family typewriter -PreparedStatement -\family default - Class -\end_layout - -\begin_layout Standard -Represents a SQL Statement that can be prepared (compiled) once and executed - many times with different parameter values. - It cannot be directly instantiated. -\end_layout - -\begin_layout Standard -It must be noticed that the methods to set the parameters should be used - with the right data type of the table column. - Otherwise, a -\family typewriter -DriverException -\family default - will be raised. - The only exception is concerning -\family typewriter -setString() -\family default -, which can be used to set any type except for -\family typewriter -blob -\family default -. - A -\family typewriter -DriverException -\family default - will also be thrown if an I -\backslash -O error occurs. - A -\family typewriter -SQLParseException -\family default - will occur whenever invalid SQL, -\family typewriter -date -\family default -, -\family typewriter -datetime -\family default - or invalid number are used. - An -\family typewriter -IllegalStateException -\family default - will be thrown whenever a closed driver or prepared statement is accessed, - whereas a -\begin_inset Newline newline -\end_inset - - -\family typewriter -IllegalArgumentException -\family default - will be raised if an invalid parameter index is used. -\end_layout - -\begin_layout Subsection - -\family typewriter -executeQuery() -\end_layout - -\begin_layout Standard - -\family typewriter -public ResultSet -\series bold -executeQuery -\series default -() -\end_layout - -\begin_layout Standard -This method executes a prepared SQL query. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -ResultSet -\family default - of the SQL statement. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the SQL used to prepare the prepared statement is not a select or not - all the parameters are defined. -\end_layout - -\begin_layout Subsection - -\family typewriter -executeUpdate() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -executeUpdate -\series default -() -\end_layout - -\begin_layout Standard -This method executes a SQL -\family typewriter -insert -\family default -, -\family typewriter -update -\family default - or -\family typewriter -delete -\family default - statement. - SQL statements that return nothing such as SQL DDL statements can also - be executed. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The result is either the row count for -\family typewriter -insert -\family default -, -\family typewriter -update -\family default - or -\family typewriter -delete -\family default - statements; or 0 for SQL statements that return nothing. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the SQL used to prepare the prepared statement is a select or not all - the parameters are defined. -\end_layout - -\begin_layout Subsection - -\family typewriter -setShort() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setShort -\series default -(int index, short value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -short -\family default - value. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -index -\family default -: the index of the parameter value to set, starting from 0. -\end_layout - -\begin_layout Itemize - -\family typewriter -value -\family default -: the value of the parameter. -\end_layout - -\begin_layout Subsection - -\family typewriter -setInt() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setInt -\series default -(int index, int value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -int -\family default - value, similar to the above method. - -\end_layout - -\begin_layout Subsection - -\family typewriter -setLong() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setLong -\series default -(int index, long value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -long -\family default - value, similar to the above method. - -\end_layout - -\begin_layout Subsection - -\family typewriter -setFloat() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setFloat -\series default -(int index, double value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -float -\family default - value, similar to the above method. - -\end_layout - -\begin_layout Subsection - -\family typewriter -setDouble() -\end_layout - -\begin_layout Standard - -\family typewriter -public void setDouble(int index, double value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -double -\family default - value, similar to the above method. - -\end_layout - -\begin_layout Subsection - -\family typewriter -setString() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setString -\series default -(int index, String value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -String -\family default - value, similar to the above method. - Moreover, this method can be used for -\family typewriter -date -\family default - and -\family typewriter -datetime -\family default - field types, where dates must be set in the format YMD. - Notice that a null string can't be set in a -\family typewriter -where -\family default - clause, otherwise a -\family typewriter -SQLParseException -\family default - will be thrown. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance(creatorId); -\end_layout - -\begin_layout Plain Layout - -driver.execute("CREATE TABLE company1(name CHAR(32), birth DATE, years DATETIME)" -); -\end_layout - -\begin_layout Plain Layout - -ps = driver.prepareStatement("INSERT INTO company1 VALUES(?, ?, ?)"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(0, "maria marlene"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(1, "2006/05/02"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(2, "2005/04/11 12:15:27:102"); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -setBlob() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setBlob -\series default -(int index, byte[] value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -byte[] -\family default - value, which is a -\family typewriter -blob -\family default -, similar to the above method. - This method will throw a -\family typewriter -SQLParseException -\family default - if one tries to set a -\family typewriter -blob -\family default - type in a parameter in a -\family typewriter -where -\family default - clause. - That is, -\family typewriter -setBlob() -\family default - can only be used with -\family typewriter -update -\family default -s and -\family typewriter -insert -\family default -s, never with -\family typewriter -delete -\family default -s or -\family typewriter -select -\family default -s. - -\end_layout - -\begin_layout Subsection - -\family typewriter -setDate() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setDate -\series default -(int index, Date value) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -Date -\family default - value, similar to the above method. - Again, a -\family typewriter -null -\family default - date can't be set in a -\family typewriter -where -\family default - clause. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance(creatorId); -\end_layout - -\begin_layout Plain Layout - -driver.execute("CREATE TABLE company1(name CHAR(32), birth DATE)"); -\end_layout - -\begin_layout Plain Layout - -ps = driver.prepareStatement("INSERT INTO company1 VALUES(?, ?)"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(0, "Ana Franscica"); -\end_layout - -\begin_layout Plain Layout - -ps.setDate(1, new Date(2006/05/06, Settings.DATE_DMY)); -\end_layout - -\end_inset - - -\series bold - -\begin_inset Box Frameless -position "t" -hor_pos "c" -has_inner_box 1 -inner_pos "t" -use_parbox 0 -use_makebox 0 -width "100col%" -special "none" -height "1in" -height_special "totalheight" -status open - -\begin_layout Plain Layout - -\series bold -ATTENTION -\series default -: One must use with care the constructor new -\family typewriter -Date() -\family default -. - Some devices can construct different dates, according to the device's date - format. - For example, the constructor -\family typewriter -new Date(12/09/2006) -\family default -, depending on the device's date format, can generate a date like 12 of - September of 2006 or 09 of December of 2006. - To avoid this, use the constructor new -\family typewriter -Date(, Settings.DATE_XXX) -\family default - instead, where -\family typewriter -Settings.DATE_XXX -\family default - is a date format parameter that must be one of the -\family typewriter -Settings.DATE_XXX -\family default - constants. -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -setDateTime() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setDateTime -\series default -(int index, Date date) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -Date -\family default -, similar to the above method. - Again, a -\family typewriter -null -\family default - -\family typewriter -date -\family default - can't be set in a -\family typewriter -where -\family default - clause. - Additionally, to set -\family typewriter -null -\family default -, a type cast to -\family typewriter -Date -\family default - must be done because to remove ambiguity with the next method. -\end_layout - -\begin_layout Standard - -\family typewriter -Date -\family default - -\family typewriter -date -\family default - will be stored as a -\family typewriter -datetime -\family default - Litebase field with 0 in its time part. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance(creatorId); -\end_layout - -\begin_layout Plain Layout - -driver.execute("CREATE TABLE company1(name CHAR(32), years DATETIME)"); -\end_layout - -\begin_layout Plain Layout - -ps = driver.prepareStatement("INSERT INTO company1 VALUES(?, ?)"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(0, "jose sebastiao"); -\end_layout - -\begin_layout Plain Layout - -ps.setDateTime(1, new Date(20060506)); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -setDateTime() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setDateTime -\series default -(int index, Time time) -\end_layout - -\begin_layout Standard -This method sets the specified parameter from the given -\family typewriter -Time -\family default -, similar to the above method. - Again, a -\family typewriter -null datetime -\family default - can't be set in a -\family typewriter -where -\family default - clause. - Additionally, to set -\family typewriter -null -\family default -, a type cast to -\family typewriter -Time -\family default - must be done because to remove ambiguity with the previous method. - -\end_layout - -\begin_layout Standard - -\family typewriter -Time time -\family default - will be stored as a -\family typewriter -datetime -\family default - Litebase field. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance(creatorId); -\end_layout - -\begin_layout Plain Layout - -driver.execute("CREATE TABLE company1(name CHAR(32), years DATETIME)"); -\end_layout - -\begin_layout Plain Layout - -ps = driver.prepareStatement("INSERT INTO company1 VALUES(?, ?)"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(0, "jose sebastiao"); -\end_layout - -\begin_layout Plain Layout - -ps.setTime(1, new Time(2006, 02, 01, 12, 15, 24, 352)); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -setNull() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setNull -\series default -(int index) -\end_layout - -\begin_layout Standard -This method sets the specified parameter to -\family typewriter -null -\family default -. - This can be used to set any column type as -\family typewriter -null -\family default -. - It must be just remembered that a parameter in a -\family typewriter -where -\family default - clause can't be set to -\family typewriter -null -\family default -. - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -index -\family default -: the index of the parameter value to set, starting from 0. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -LitebaseConnection driver = LitebaseConnection.getInstance(creatorId); -\end_layout - -\begin_layout Plain Layout - -driver.execute("CREATE TABLE company1(name CHAR(32), years DATETIME)"); -\end_layout - -\begin_layout Plain Layout - -ps = driver.prepareStatement("INSERT INTO company1 VALUES(?, ?)"); -\end_layout - -\begin_layout Plain Layout - -ps.setString(0, "jose sebastiao"); -\end_layout - -\begin_layout Plain Layout - -ps.setNull(1); -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -clearParameters() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -clearParameters -\series default -() -\end_layout - -\begin_layout Standard -This method clears all of the input parameters that have been set on this - statement. - -\end_layout - -\begin_layout Subsection - -\family typewriter -toString() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -toString -\series default -() -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The SQL used in this statement. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -PreparedStatement psInsert, psUpdate, psDelete, psSelect; -\end_layout - -\begin_layout Plain Layout - -String sqlInsert, sqlUpdate, sqlDelete, sqlSelect; -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// driver is the LitebaseConnection instantiated above. -\end_layout - -\begin_layout Plain Layout - -// Call this method in the start of the application, after Litebase has - been initializated and after the table has been created. -\end_layout - -\begin_layout Plain Layout - -public void preparedStatement() -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - try -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - sqlInsert = "INSERT INTO person VALUES (?, ?, ?, ?, ?, ?, ?)"; -\end_layout - -\begin_layout Plain Layout - - sqlUpdate = "UPDATE person SET name = ?, birthday = ?, salary = ?, gender - = ?, married = ?, age = ?, region = ? WHERE rowid = ?"; -\end_layout - -\begin_layout Plain Layout - - sqlDelete = "DELETE FROM person WHERE rowid = ?"; -\end_layout - -\begin_layout Plain Layout - - sqlSelect = "SELECT * FROM person WHERE age >= ?"; -\end_layout - -\begin_layout Plain Layout - - psInsert = driver.prepareStatement(sqlInsert); -\end_layout - -\begin_layout Plain Layout - - psUpdate = driver.prepareStatement(sqlUpdate); -\end_layout - -\begin_layout Plain Layout - - psDelete = driver.prepareStatement(sqlDelete); -\end_layout - -\begin_layout Plain Layout - - psSelect = driver.prepareStatement(sqlSelect); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - catch(Exception exception) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - /*...*/ -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\begin_layout Plain Layout - -// This method fills the prepared statement and executes them. -\end_layout - -\begin_layout Plain Layout - -public void setPreparedStatement() -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - try -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - psInsert.setString(0, "Zenes Novais"); -\end_layout - -\begin_layout Plain Layout - - psInsert.setDate(1, "1980/05/03"); -\end_layout - -\begin_layout Plain Layout - - psInsert.setDouble(2, 3540.0); -\end_layout - -\begin_layout Plain Layout - - psInsert.setString(3, "F"); -\end_layout - -\begin_layout Plain Layout - - psInsert.setString(4, "Yes"); -\end_layout - -\begin_layout Plain Layout - - psInsert.setInt(5, 26); -\end_layout - -\begin_layout Plain Layout - - psInsert.setInt(6, 8); -\end_layout - -\begin_layout Plain Layout - - psInsert.executeUpdate(); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setString(0, "Jener Novais"); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setDate(1, "1979/05/03"); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setDouble(2, 3800.0); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setString(3, "M"); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setString(4, "Yes"); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setInt(5, 27); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setInt(6, 8); -\end_layout - -\begin_layout Plain Layout - - psUpdate.setInt(7, 4); // Updates the row of rowid = 4. -\end_layout - -\begin_layout Plain Layout - - psUpdate.executeUpdate(); -\end_layout - -\begin_layout Plain Layout - - psDelete.setInt(0, 3); // Drops the row of rowid = 3. -\end_layout - -\begin_layout Plain Layout - - psDelete.executeUpdate(); -\end_layout - -\begin_layout Plain Layout - - psSelect.setInt(0, 25); // Sets the age in the select. -\end_layout - -\begin_layout Plain Layout - - ResultSet rs = psSelect.executeQuery(); -\end_layout - -\begin_layout Plain Layout - - while (rs.next) -\end_layout - -\begin_layout Plain Layout - - Vm.debug("Age:" + rs.getShort(age)); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - catch(Exception exception) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - /*...*/ -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -close() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -close -\series default -() -\end_layout - -\begin_layout Standard -Closes a prepared statement. - This is important if the application creates too many prepared statements - during one run, since they are only closed if their connection is closed. -\end_layout - -\begin_layout Subsection - -\family typewriter -isValid() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isValid -\series default -() -\end_layout - -\begin_layout Standard -Checks if a prepared statement is still valid. - This happens if the driver and the prepared statement have not been closed - and the table was not dropped. - -\end_layout - -\begin_layout Section - -\family typewriter -ResultSetMetaData -\family default - Class -\begin_inset CommandInset label -LatexCommand label -name "sec:ResultSetMetaData-Class" - -\end_inset - - -\end_layout - -\begin_layout Standard -This class returns useful information for the result set columns. - It cannot be directly instantiated. - An -\family typewriter -IllegalStateException -\family default - will be thrown if the driver or result set are closed, whereas an -\family typewriter -IllegalArgumentException -\family default - will be thrown if an invalid column index is used. - A -\family typewriter -DriverException -\family default - will be thrown if an I -\backslash -O error occurs. -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnCount() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getColumnCount -\series default -() -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The number of columns for this result set. -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnDisplaySize() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getColumnDisplaySize -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The display size. - For string types, it will return the number of characters defined; for - primitive types, it will return the number of decimal places it needs to - be displayed correctly. - Returns -1 if the column type is -\family typewriter -blob -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnLabel() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getColumnLabel -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The column name. - Note that if an alias for the column is used, the alias will be returned - instead. - -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnType() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getColumnType -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The column type. - Its values can be: -\family typewriter -SHORT_TYPE -\family default -, -\family typewriter -INT_TYPE -\family default -, -\family typewriter -LONG_TYPE -\family default -, -\family typewriter -FLOAT_TYPE -\family default -, -\family typewriter -DOUBLE_TYPE -\family default -, -\family typewriter -CHAR_TYPE -\family default -, -\family typewriter -CHAR_NOCASE_TYPE -\family default -, -\family typewriter -DATE_TYPE -\family default -, -\family typewriter -DATETIME_TYPE -\family default -, and -\family typewriter -BLOB_TYPE -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Example: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -// This method fills the attributes below with table meta data. -\end_layout - -\begin_layout Plain Layout - -// - columnsName (contains names of columns). -\end_layout - -\begin_layout Plain Layout - -// - columnsLength (contain length of columns) for types different from - char. -\end_layout - -\begin_layout Plain Layout - -// For example INT/DOUBLE will have columnsLength = max value for the TYPE - (e. - g.: DOUBLE = 21) -\end_layout - -\begin_layout Plain Layout - -// - columnsType (contains types of columns). - -\end_layout - -\begin_layout Plain Layout - -private void discoverMetaDataTable(String[] columnsName, int[] columnsLength, - int[] columnsType) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - ResultSet rs = null; -\end_layout - -\begin_layout Plain Layout - - try -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - openLb(); // Gets the instance of Litebase. -\end_layout - -\begin_layout Plain Layout - - rs = lb.executeQuery("SELECT * FROM person WHERE rowid = 10000"); -\end_layout - -\begin_layout Plain Layout - - ResultSetMetaData rsmd = rs.getResultSetMetaData(); -\end_layout - -\begin_layout Plain Layout - - int numCols = rsmd.getColumnCount(); // Gets the number of cols. -\end_layout - -\begin_layout Plain Layout - - -\end_layout - -\begin_layout Plain Layout - - // Fills the parameters. -\end_layout - -\begin_layout Plain Layout - - for (int i = 1; i <= numCols; i++) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - columnsName[i - 1] = rsmd.getColumnLabel(i); -\end_layout - -\begin_layout Plain Layout - - columnsType[i - 1] = rsmd.getColumnType(i); -\end_layout - -\begin_layout Plain Layout - - columnsLength[i - 1] = rsmd.getColumnDisplaySize(i); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - catch(Exception exception) -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - handleException(exception); -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - - finally -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - if (rs != null) -\end_layout - -\begin_layout Plain Layout - - rs.close(); -\end_layout - -\begin_layout Plain Layout - - closeLb(); // Closes Litebase. -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnTypeName() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getColumnTypeName -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The name of the column type. -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnTableName() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getColumnTableName -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The name of the table it came from. -\end_layout - -\begin_layout Subsection - -\family typewriter -getColumnTableName() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getColumnTableName -\series default -(String columnName) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnName -\family default -: Column name or alias. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The name of the table it came from. - It is important to notice that if the query is a join and two fields in - the result set have the same name, only the name of the first table of - this column name will be returned. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column name is not found. -\end_layout - -\begin_layout Subsection - -\family typewriter -hasDefaultValue() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -hasDefaultValue -\series default -(int columnIdx) -\end_layout - -\begin_layout Standard -Indicates if a column of the result set has a default value. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnIdx -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the given column of the result set has a default value; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column does not have an underlining table column, such as -\family typewriter -count(*) -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -hasDefaultValue() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -hasDefaultValue -\series default -(String columnName) -\end_layout - -\begin_layout Standard -Indicates if a column of the result set has a default value. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnName -\family default -: The column name or alias. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the given column of the result set has a default value; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column does not have an underlining table column, such as -\family typewriter -count(*) -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -isNotNull() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isNotNull -\series default -(int columnIdx) -\end_layout - -\begin_layout Standard -Indicates if a column of the result is declared as -\family typewriter -not null -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnIdx -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the given column of the result set is declared as -\family typewriter -not null -\family default -; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column does not have an underlining table column, such as -\family typewriter -count(*) -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -isNotNull() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isNotNull -\series default -(String columnName) -\end_layout - -\begin_layout Standard -Indicates if a column of the result set is declared as -\family typewriter -not null -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnName -\family default -: The column name or alias. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the given column of the result set is declared as -\family typewriter -not null -\family default -; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column does not have an underlining table column, such as -\family typewriter -count(*) -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDefaultValue() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getDefaultValue -\series default -(int columnIndex) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnIdx -\family default -: The column index (starting at 1). -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The default value of the column as a string or -\family typewriter -null -\family default - if it does not have a default value. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column does not have an underlining table column, such as -\family typewriter -count(*) -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDefaultValue() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getDefaultValue -\series default -(String columnName) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -columnName -\family default -: The column name or alias. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The default value of the column as a string or -\family typewriter -null -\family default - if it does not have a default value. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the column does not have an underlining table column, such as -\family typewriter -count(*) -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getPKColumnIndices() -\end_layout - -\begin_layout Standard - -\family typewriter -public byte[] -\series bold -getPKColumnIndices -\series default -(String tableName) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name or alias. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -null -\family default - if the given table does not have primary key or an array with the column - indices of the primary key. -\end_layout - -\begin_layout Standard -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the given table name is not part of the query of this result set. -\end_layout - -\begin_layout Subsection - -\family typewriter -getPKColumnNames() -\end_layout - -\begin_layout Standard - -\family typewriter -public String[] -\series bold -getPKColumnNames -\series default -(String tableName) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -tableName -\family default -: The table name or alias. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -null -\family default - if the given table does not have primary key or an array with the column - names of the primary key. -\end_layout - -\begin_layout Standard -Throws: -\end_layout - -\begin_layout Itemize - -\family typewriter -DriverException -\family default -: If the given table name is not part of the query of this result set. -\end_layout - -\begin_layout Section - -\family typewriter -RowIterator -\family default - Class -\end_layout - -\begin_layout Standard -Class used to iterate through the rows of a database. - It can access some attributes from the row that ease the control of which - row was changed, deleted, or is newer since a synchronization. - It can also be used to access the values of the columns of the current - row. -\end_layout - -\begin_layout Standard -An iterator cannot be constructed directly; it must be created through the - method -\begin_inset Newline newline -\end_inset - - -\family typewriter -LitebaseConnection.getIterator() -\family default -. -\end_layout - -\begin_layout Standard -An -\family typewriter -IllegalStateException -\family default - will be thrown if the driver or row iterator are closed, whereas an -\family typewriter -IllegalArgumentException -\family default - will be thrown if an invalid column index is used. - A -\family typewriter - -\begin_inset Newline newline -\end_inset - -DriverException -\family default - will be thrown if an I -\backslash -O error occurs or a get method is used with the wrong type. -\end_layout - -\begin_layout Standard -Useful members: -\end_layout - -\begin_layout Subsection - -\family typewriter -data -\end_layout - -\begin_layout Standard - -\family typewriter -public byte[] -\series bold -data -\end_layout - -\begin_layout Standard -The data for the current row. - The whole row is included. -\end_layout - -\begin_layout Subsection - -\family typewriter -rowid -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -rowid -\end_layout - -\begin_layout Standard -The rowid for the current row. -\end_layout - -\begin_layout Subsection - -\family typewriter -attr -\end_layout - -\begin_layout Standard - -\family typewriter -public byte -\series bold -attr -\end_layout - -\begin_layout Standard -The attribute for this row. - The constants -\family typewriter -ROW_ATTR_SYNCED -\family default -, -\family typewriter -ROW_ATTR_NEW -\family default -, -\family typewriter -ROW_ATTR_UPDATED -\family default -, or -\family typewriter -ROW_ATTR_DELETED -\family default - must be used for comparison. - It must be noticed that changing this attribute does not change the current - row attribute. - That is, this should be READ ONLY. -\end_layout - -\begin_layout Subsection - -\family typewriter -rowNumber -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -rowNumber -\end_layout - -\begin_layout Standard -The number of the row. - This must be READ ONLY. - Changing it will corrupt your database. -\end_layout - -\begin_layout Standard -Methods available: -\end_layout - -\begin_layout Subsection - -\family typewriter -next() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -next -\series default -() -\end_layout - -\begin_layout Standard -Moves to the next record and fills the data members. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if it is possible to iterate to the next record; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -nextNotSynced() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -nextNotSynced -\series default -() -\end_layout - -\begin_layout Standard -Moves to the next record with an attribute different of -\family typewriter -ROW_ATTR_SYNCED -\family default -. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if it is possible to iterate to a next record not synchronized; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -setSynced() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setSynced -\series default -() -\end_layout - -\begin_layout Standard -If the attribute is currently -\family typewriter -NEW -\family default - or -\family typewriter -UPDATED -\family default -, this method sets them to -\family typewriter -SYNCED -\family default -. - Note that if the row is -\family typewriter -DELETED -\family default -, the change will be ignored. - That is, deleted rows are always marked as not synchronized. - Rows excluded by recover table will be marked as -\family typewriter -DELETED -\family default - and its -\family typewriter -rowid -\family default - changed to zero. - These rows must be ignored when synchronizing the table. - In most cases they will be spurious or blank rows. -\end_layout - -\begin_layout Subsection - -\family typewriter -close() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -close -\series default -() -\end_layout - -\begin_layout Standard -Closes this iterator. - After calling this method it is possible to do queries in a safe way. -\end_layout - -\begin_layout Subsection - -\family typewriter -reset() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -reset -\series default -() -\end_layout - -\begin_layout Standard -Resets the counter to zero so that it is possible to restart to fetch records. -\end_layout - -\begin_layout Standard -Example: -\end_layout - -\begin_layout Standard -Note: There is another example about this topic in -\family typewriter -LitebaseConnection.getRowIterator() -\family default - -\begin_inset CommandInset ref -LatexCommand vpageref -reference "sub:getRowIterator()" - -\end_inset - -. -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -private static final char INSERTED = 'I'; -\end_layout - -\begin_layout Plain Layout - -private static final char DELETED = 'D'; -\end_layout - -\begin_layout Plain Layout - -private static final char UPDATED = 'U'; -\end_layout - -\begin_layout Plain Layout - -private static final char SYNC = 'S'; -\end_layout - -\begin_layout Plain Layout - -\end_layout - -\begin_layout Plain Layout - -// Returns a char correspondent to RowIterator.attr -\end_layout - -\begin_layout Plain Layout - -private char attrToChar(RowIterator ri) -\end_layout - -\begin_layout Plain Layout - -{ -\end_layout - -\begin_layout Plain Layout - - switch (ri.attr) // USE SWITCH -\end_layout - -\begin_layout Plain Layout - - { -\end_layout - -\begin_layout Plain Layout - - case RowIterator.ROW_ATTR_NEW: -\end_layout - -\begin_layout Plain Layout - - return INSERTED; -\end_layout - -\begin_layout Plain Layout - - case RowIterator.ROW_ATTR_UPDATED: -\end_layout - -\begin_layout Plain Layout - - return UPDATED; -\end_layout - -\begin_layout Plain Layout - - case RowIterator.ROW_ATTR_DELETED: -\end_layout - -\begin_layout Plain Layout - - return DELETED; -\end_layout - -\begin_layout Plain Layout - - case RowIterator.ROW_ATTR_SYNCED: -\end_layout - -\begin_layout Plain Layout - - return SYNC; -\end_layout - -\begin_layout Plain Layout - - default: -\end_layout - -\begin_layout Plain Layout - - return '-'; -\end_layout - -\begin_layout Plain Layout - - } -\end_layout - -\begin_layout Plain Layout - -} -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsection - -\family typewriter -getShort() -\end_layout - -\begin_layout Standard - -\family typewriter -public short -\series bold -getShort -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -short -\family default - value of the column. - Returns 0 if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getInt() -\end_layout - -\begin_layout Standard - -\family typewriter -public int -\series bold -getInt -\series default -(int column) -\family default - -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -int -\family default - value of the column. - Returns 0 if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getFloat() -\end_layout - -\begin_layout Standard - -\family typewriter -public double -\series bold -getFloat -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -float -\family default - value of the column. - Returns 0.0 if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDouble() -\end_layout - -\begin_layout Standard - -\family typewriter -public double -\series bold -getDouble -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -double -\family default - value of the column. - Returns 0.0 if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getString() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -getString -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -string -\family default - value of the column. - Returns -\family typewriter -null -\family default - if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDate() -\end_layout - -\begin_layout Standard - -\family typewriter -public Date -\series bold -getDate -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -date -\family default - value of the column as a -\family typewriter -Date -\family default - object. - Returns -\family typewriter -null -\family default - if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getDateTime() -\end_layout - -\begin_layout Standard - -\family typewriter -public Time -\series bold -getDateTime -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -datetime -\family default - value of the column as a -\family typewriter -Time -\family default - object. - Returns -\family typewriter -null -\family default - if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -getBlob() -\end_layout - -\begin_layout Standard - -\family typewriter -public byte[] -\series bold -getBlob -\series default -(int column) -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize -The -\family typewriter -blob -\family default - value of the column as a -\family typewriter -byte -\family default - array object. - Returns -\family typewriter -null -\family default - if the column is -\family typewriter -null -\family default -. -\end_layout - -\begin_layout Subsection - -\family typewriter -isNull() -\end_layout - -\begin_layout Standard - -\family typewriter -public boolean -\series bold -isNull -\series default -(int column) -\end_layout - -\begin_layout Standard -Indicates if the value stored in a column is null. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Parameters: -\end_layout - -\begin_layout Itemize - -\family typewriter -column -\family default -: The column index. -\end_layout - -\begin_layout Labeling -\labelwidthstring 00.00.0000 -Returns: -\end_layout - -\begin_layout Itemize - -\family typewriter -true -\family default - if the column is null; -\family typewriter -false -\family default -, otherwise. -\end_layout - -\begin_layout Subsection - -\family typewriter -setNotSynced() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setNotSynced -\series default -() -\end_layout - -\begin_layout Standard -If the attribute is currently -\family typewriter -SYNCED -\family default - or -\family typewriter -UPDATED -\family default -, this method sets them to -\family typewriter -NEW -\family default -. - Note that if the row is -\family typewriter -DELETED -\family default -, the change will be ignored. - That is, deleted rows will not be resurrected. - This method will be useful if a row was marked as synchronized but was - not sent to server for some problem. - Use this carefully. -\end_layout - -\begin_layout Part -APPENDIXES -\end_layout - -\begin_layout Chapter -\start_of_appendix -Copyright -\end_layout - -\begin_layout Standard -All the contents of this tutorial, including text, programs, applets, source - code, and images are copyrighted and are owned by SuperWaba Ltda, all rights - reserved. - No material can be reproduced and/or distributed electronically, in print - or otherwise without the express written permission of SuperWaba Ltda. -\end_layout - -\begin_layout Standard -The use of the source code examples is only permitted by the company that - purchased this subscription. -\end_layout - -\begin_layout Chapter -News -\end_layout - -\begin_layout Itemize -Removed everything concerning BlackBerry, Palm OS, and Windows CE, since - these platfroms are not supported anymore. -\end_layout - -\begin_layout Itemize -Added information concerning Windows Phone 8 memory card, which can't be - used to create databases. - -\end_layout - -\begin_layout Itemize -Removed table migration section since the table format has not been changed - for too long. -\end_layout - -\begin_layout Itemize -Added SalesPlus sample. -\end_layout - -\begin_layout Itemize -Joined PhotoDB into AddressBook. -\end_layout - -\begin_layout Section -ResultSet -\end_layout - -\begin_layout Subsection - -\family typewriter -rowToString() -\end_layout - -\begin_layout Standard - -\family typewriter -public String -\series bold -rowToString -\series default -() -\end_layout - -\begin_layout Standard -Returns a whole current row of a -\family typewriter -ResultSet -\family default - in a string with column data separated by tab. - With a column has a -\family typewriter -null -\family default - or empty value, the string will have two consecutive tabs ( -\begin_inset Quotes sld -\end_inset - - -\family typewriter - -\backslash -t -\backslash -t -\family default - -\begin_inset Quotes srd -\end_inset - -). - Blobs are treated as nulls. -\end_layout - -\begin_layout Section -RowIterator -\end_layout - -\begin_layout Subsection - -\family typewriter -setNotSynced() -\end_layout - -\begin_layout Standard - -\family typewriter -public void -\series bold -setNotSynced -\series default -() -\end_layout - -\begin_layout Standard -If the attribute is currently -\family typewriter -SYNCED -\family default - or -\family typewriter -UPDATED -\family default -, this method sets them to -\family typewriter -NEW -\family default -. - Note that if the row is -\family typewriter -DELETED -\family default -, the change will be ignored. - That is, deleted rows will not be resurrected. - This method will be useful if a row was marked as synchronized but was - not sent to server for some problem. - Use this carefully. -\end_layout - -\end_body -\end_document diff --git a/LitebaseSDK/docs/Litebase Companion.odt b/LitebaseSDK/docs/Litebase Companion.odt deleted file mode 100644 index f899df3b8f..0000000000 Binary files a/LitebaseSDK/docs/Litebase Companion.odt and /dev/null differ diff --git a/LitebaseSDK/docs/classes.jpg b/LitebaseSDK/docs/classes.jpg deleted file mode 100644 index 6656f38e3a..0000000000 Binary files a/LitebaseSDK/docs/classes.jpg and /dev/null differ diff --git a/LitebaseSDK/docs/diagrama.jpg b/LitebaseSDK/docs/diagrama.jpg deleted file mode 100644 index 005d549eb8..0000000000 Binary files a/LitebaseSDK/docs/diagrama.jpg and /dev/null differ diff --git a/LitebaseSDK/docs/images/convert.png b/LitebaseSDK/docs/images/convert.png deleted file mode 100644 index fab50d55ae..0000000000 Binary files a/LitebaseSDK/docs/images/convert.png and /dev/null differ diff --git a/LitebaseSDK/docs/images/title.png b/LitebaseSDK/docs/images/title.png deleted file mode 100644 index b1f48bb5cc..0000000000 Binary files a/LitebaseSDK/docs/images/title.png and /dev/null differ diff --git a/LitebaseSDK/docs/links.txt b/LitebaseSDK/docs/links.txt deleted file mode 100644 index e7dbd6cea3..0000000000 --- a/LitebaseSDK/docs/links.txt +++ /dev/null @@ -1,2 +0,0 @@ -diagramas em -http://www.gliffy.com diff --git a/LitebaseSDK/docs/litebase.odt b/LitebaseSDK/docs/litebase.odt deleted file mode 100644 index d35bf12552..0000000000 Binary files a/LitebaseSDK/docs/litebase.odt and /dev/null differ diff --git a/LitebaseSDK/src/java/litebase/AlreadyCreatedException.java b/LitebaseSDK/src/java/litebase/AlreadyCreatedException.java deleted file mode 100644 index 951d0a0628..0000000000 --- a/LitebaseSDK/src/java/litebase/AlreadyCreatedException.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be thrown by LitebaseConnection.execute, when a table or index has already been - * created. It is an unchecked Exception (can be thrown any time). - */ -@Deprecated -public class AlreadyCreatedException extends RuntimeException -{ - /** - * Constructs a new AlreadyCreatedException exception with the specified detail message. - * - * @param message the detail message. - */ - AlreadyCreatedException(String message) - { - super(message); - } -} diff --git a/LitebaseSDK/src/java/litebase/AlreadyCreatedException4D.java b/LitebaseSDK/src/java/litebase/AlreadyCreatedException4D.java deleted file mode 100644 index 24d3ad308b..0000000000 --- a/LitebaseSDK/src/java/litebase/AlreadyCreatedException4D.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be thrown by LitebaseConnection.execute, when a table or index has already been - * created. It is an unchecked Exception (can be thrown any time). - */ -@Deprecated -public class AlreadyCreatedException4D extends RuntimeException -{ -} diff --git a/LitebaseSDK/src/java/litebase/ComposedIndex.java b/LitebaseSDK/src/java/litebase/ComposedIndex.java deleted file mode 100644 index 9e5d41a699..0000000000 --- a/LitebaseSDK/src/java/litebase/ComposedIndex.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Represents a composed index. - */ -@Deprecated -class ComposedIndex -{ - /** - * Identifies the composed index. - */ - int indexId; - - /** - * The columns index of the composed index. - */ - byte[] columns; - - /** - * The index itself. - */ - Index index; - - /** - * Creates a composed index. - * - * @param id The index id. - * @param newColumns The columns of this index. - */ - ComposedIndex(int id, byte[] NewColumns) - { - indexId = id; - columns = NewColumns; - } -} diff --git a/LitebaseSDK/src/java/litebase/DataStreamLB.java b/LitebaseSDK/src/java/litebase/DataStreamLB.java deleted file mode 100644 index 2b86665ef9..0000000000 --- a/LitebaseSDK/src/java/litebase/DataStreamLB.java +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; - -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Is the same of DataStreamLE except for being able to use cryptography. - */ -@Deprecated -class DataStreamLB extends DataStreamLE -{ - /** - * Indicates that is to use cryptography. - */ - boolean useCrypto; - - /** - * Constructs a new DataStreamLB which sits upon the given stream using little - * endian notation for multibyte values. - * - * @param stream the base stream. - * @param newUseCrypto Indicates that is to use cryptography. - */ - public DataStreamLB(Stream stream, boolean newUseCrypto) - { - super(stream); - useCrypto = newUseCrypto; - } - - /** - * This method reads an exact amount of bytes from the underlying stream. - * - * @param buffer The buffer to be read. - * @param start The first position of the stream to be read. - * @param count The number of bytes to be read. - * @param throwEOF Indicates if an EOFException should be thrown if an EOF is found. - * @return The number of bytes read: count. - * @throws IOException If the stream reaches its end before all bytes are read. - */ - protected int readBytesInternal(byte[] buffer, int start, int count, boolean throwEOF) throws IOException - { - super.readBytesInternal(buffer, start, count, throwEOF); - - if (useCrypto) // Decrypts data if necessary. - while (start < count) - buffer[start++] ^= 0xAA; - - return count; - } - - /** - * This method writes an exact amount of bytes into the underlying stream. - * - * @param buffer The buffer whose data is to be written. - * @param start The first position of the stream to be written. - * @param count The number of bytes to be written. - * @throws IOException If an error occurs. - */ - protected int writeBytesInternal(byte buf[], int start, int count) throws IOException - { - if (useCrypto) // Encrypts data if asked. - { - int i = start; - while (i < count) - buf[i++] ^= 0xAA; - } - - int ret = super.writeBytesInternal(buf, start, count); - - if (useCrypto) // Decrypts data if necessary. - while (start < count) - buf[start++] ^= 0xAA; - - return ret; - } -} diff --git a/LitebaseSDK/src/java/litebase/DriverException.java b/LitebaseSDK/src/java/litebase/DriverException.java deleted file mode 100644 index 4e417058ca..0000000000 --- a/LitebaseSDK/src/java/litebase/DriverException.java +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be dispatched if any problem, other than a SQL parsing error, occurs. It is an unchecked Exception - * (it can be thrown any time). - */ -@Deprecated -public class DriverException extends RuntimeException -{ - /** - * The exception that caused this exception to be dispatched, or null if the cause of this exception was not another exception. - */ - public Exception cause; - - /** - * Constructs a new DriverException exception with the specified detail message. - * - * @param message the detail message. - */ - DriverException(String message) - { - super(message); - } - - /** - * Constructs a new DriverException exception with a detail message taken from the given exception. The cause can be accessed in the - * public member cause. Use it to get a better stack trace to the problem. - * - * @param causeException the exception that caused this one. - */ - DriverException(Exception causeException) - { - this(causeException.getMessage()); - cause = causeException; - } - - /** - * Prints this stack trace and also the trace of the cause, if any was set. - */ - public void printStackTrace() - { - if (cause != null) - cause.printStackTrace(); - super.printStackTrace(); - } -} diff --git a/LitebaseSDK/src/java/litebase/DriverException4D.java b/LitebaseSDK/src/java/litebase/DriverException4D.java deleted file mode 100644 index afad43ad71..0000000000 --- a/LitebaseSDK/src/java/litebase/DriverException4D.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -// juliana@223_10: corrected a bug of not finding DriverException -/** - * This exception may be dispatched if any problem, other than a SQL parsing error, occurs. It is an unchecked Exception - * (it can be thrown any time). - */ -@Deprecated -public class DriverException4D extends RuntimeException -{ - /** - * The exception that caused this exception to be dispatched, or null if the cause of this exception was not another exception. - */ - public Exception cause; - - /** - * Constructs a new DriverException exception with the specified detail message. - * - * @param message the detail message. - */ - DriverException4D(String message) - { - super(message); - } -} diff --git a/LitebaseSDK/src/java/litebase/Entry.java b/LitebaseSDK/src/java/litebase/Entry.java deleted file mode 100644 index 3eb21c3700..0000000000 --- a/LitebaseSDK/src/java/litebase/Entry.java +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Hash table for Litebase SQL reserved words collision list. - */ -@Deprecated -class Entry -{ - /** - * The hash code of the reserved word string. - */ - int hash; - - /** - * The token code of the SQL reserved word. - */ - int value; - - /** - * The reserved word string. - */ - String key; - - /** - * The next entry of the collision list. - */ - Entry next; -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/litebase/Index.java b/LitebaseSDK/src/java/litebase/Index.java deleted file mode 100644 index 85758a675e..0000000000 --- a/LitebaseSDK/src/java/litebase/Index.java +++ /dev/null @@ -1,1074 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.util.*; - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * Represents a B-Tree header. - */ -@Deprecated -class Index -{ - /** - * The size of the index cache. - */ - private static final int INDEX_CACHE_SIZE = 20; - - /** - * The size of the disk sector. Used for calculating the number of keys of a node. - */ - private static final int SECTOR_SIZE = 512; - - /** - * The sizes for each type. - */ - private static final int[] keyRecSizes = {4, 2, 4, 8, 4, 8, 4, 0, 4, 8, 0}; - - /** - * The maximun number of keys per node. - */ - int btreeMaxNodes; - - /** - * The root of the tree. - */ - Node root; - - /** - * A temporary key. - */ - Key tempKey; - - /** - * Indicates if the index is still empty. - */ - private boolean isEmpty; - - /** - * The size of the nodes. - */ - int nodeRecSize; - - /** - * A cache of node. - */ - private int cacheI; - - /** - * The size of the keys. - */ - int keyRecSize; - - /** - * The number of nodes. - */ - int nodeCount; - - /** - * The current number of nodes in the nodes array. - */ - int nodesArrayCount; - - /** - * The sizes of the columns of the index. - */ - int[] colSizes; - - /** - * The types of the columns of the index. - */ - byte[] types; - - // juliana@253_6: The maximum number of keys of a index was duplicated. - /** - * The cache of the index. - */ - private Node[] cache = new Node[INDEX_CACHE_SIZE]; // Creates the cache.; - - /** - * The first level of the index B-tree. - */ - Node[] firstLevel; // juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - - /** - * The name of the index table. - */ - String name; - - /** - * The nodes file. - */ - NormalFile fnodes; - - /** - * A stream to be used to save and load data from the index. - */ - ByteArrayStream bas; - - /** - * A stream to be used to save and load data from the index. - */ - DataStreamLB basds; - - /** - * A buffer to be used to save and load data from the index. - */ - byte[] basbuf; - - /** - * If the keys are mostly ordered (like the rowid), makes the nodes more full. - */ - boolean isOrdered; // guich@110_5 - - /** - * Indicates if the write of the node is delayed. - */ - boolean isWriteDelayed; - - /** - * The table of the index. - */ - Table table; - - /** - * An array for climbing on index nodes. - */ - private Node[] nodes = new Node[4]; - - /** - * Constructs an index structure. - * - * @param aTable The table of the index. - * @param keyTypes The types of the columns of the index. - * @param newColSizes The column sizes. - * @param aName The name of the index table. - * @param sourcePath The path of the index files. - * @param exist Indicates that the index files already exist. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - Index(Table aTable, byte[] keyTypes, int[] newColSizes, String aName, String sourcePath, boolean exist) - throws IOException, InvalidDateException - { - int numberColumns = keyTypes.length; - - table = aTable; - types = keyTypes; - name = aName; - colSizes = newColSizes; - keyRecSize = Key.VALREC_SIZE; - - while (--numberColumns >= 0) // Gets the key sizes for each column of the index. - keyRecSize += keyRecSizes[keyTypes[numberColumns]]; - - btreeMaxNodes = (SECTOR_SIZE - 5) / (keyRecSize + 2); - nodeRecSize = 2 + btreeMaxNodes * keyRecSize + ((btreeMaxNodes + 1) << 1); // int size + key[k] + (Node = int)[k+1] - - // Creates the streams. - basbuf = (bas = new ByteArrayStream(nodeRecSize)).getBuffer(); - basds = new DataStreamLB(bas, aTable.db.useCrypto); - - firstLevel = new Node[btreeMaxNodes]; // Creates the first index level. // juliana@230_35 - - // Creates the index files. - String fullFileName = Utils.getFullFileName(name, sourcePath); - fnodes = new NormalFile(fullFileName + ".idk", !exist, nodeRecSize); - - // Creates the root node. - root = new Node(this); - root.idx = 0; - - if (!(isEmpty = fnodes.size == 0)) - root.load(); - - // juliana@213_8: the index node count was not being loaded when loading the indices, which could cause an infinite loop when using them. - nodeCount = fnodes.size / nodeRecSize; - - tempKey = new Key(this); // Creates the temp key. - } - - /** - * Removes a value from the index. - * - * @param key The key to be removed. - * @param record The repeated value record index. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws DriverException If the index is corrupted. - */ - void removeValue(Key key, int record) throws IOException, InvalidDateException, DriverException - { - if (!isEmpty) - { - Node curr = root; // 0 is always the root. - int pos, - nodeCounter = nodeCount, - firstChild, - size, - count = 0; - byte[] typesAux = types; - SQLValue[] keys = key.keys; - int[] children; - Key[] currKeys; - Key keyFound; - PlainDB plainDB = table.db; - int[] vector = plainDB.driver.nodes; - - while (true) - { - keyFound = (currKeys = curr.keys)[pos = curr.findIn(key, false)]; // juliana@201_3 // Finds the key position. - firstChild = (children = curr.children)[0]; - - if (pos < (size = curr.size) && Utils.arrayValueCompareTo(keys, keyFound.keys, typesAux, plainDB) == 0) - { - while (pos >= 0 && Utils.arrayValueCompareTo(keys, (keyFound = currKeys[pos]).keys, typesAux, plainDB) == 0 - && (keyFound.record >= record || keyFound.record == Key.NO_VALUE)) - pos--; - while (++pos < size && Utils.arrayValueCompareTo(keys, (keyFound = currKeys[pos]).keys, typesAux, plainDB) == 0 - && (keyFound.record <= record || keyFound.record == Key.NO_VALUE)) - { - if (record == keyFound.record) - { - keyFound.record = Key.NO_VALUE; // Tries to remove the key. - curr.saveDirtyKey(pos); - return; - } - - if (keyFound.record == Key.NO_VALUE && firstChild != Node.LEAF) - vector[count++] = children[pos]; - } - } - - // If there are children, load them if the key was not found yet. - if (firstChild != Node.LEAF) - vector[count++] = children[pos]; - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - - if (count > 0) - curr = loadNode(vector[--count]); - else - break; - } - - } - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_IDX_RECORD_DEL)); - } - - /** - * Loads a node. - * - * @param idx The index of the value to be loaded. - * @return The node. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws DriverException If the index is corrupted. - */ - private Node loadNode(int idx) throws IOException, InvalidDateException, DriverException - { - if (idx == 0) // If the index is 0, return the root. - return root; - if (idx == Node.LEAF) // If the node is a leaf, the index is corrupted. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - - Node cand; - - // juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - // Tries to find the node in the nodes of the first level. - if (idx <= btreeMaxNodes) - { - if ((cand = firstLevel[idx - 1]) == null) - { - (cand = firstLevel[idx - 1] = new Node(this)).idx = idx; - cand.load(); - } - else if (cand.idx == -1) - { - cand.idx = idx; - cand.load(); - } - return cand; - } - - // Loads the cache if the node is in a deeper level. - Node[] cacheAux = cache; - int j = INDEX_CACHE_SIZE; - while (--j >= 0) - if (cacheAux[j] != null && cacheAux[j].idx == idx) - return cacheAux[cacheI = j]; - - if (++cacheI >= INDEX_CACHE_SIZE) - cacheI = 0; - if ((cand = cacheAux[cacheI]) == null) - cand = cacheAux[cacheI] = new Node(this); - - if (isWriteDelayed && cand.isDirty) // Saves this one if it is dirty. - cand.save(false, 0, cand.size); - - // Loads the node. - cand.idx = idx; - cand.load(); - return cand; - } - - /** - * Finds the given key and marks the records that are going to the result set. - * - * @param key The key to be found. - * @param markBits The rows which will be returned to the result set. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws DriverException If the index is corrupted. - */ - void getValue(Key key, MarkBits markBits) throws IOException, InvalidDateException, DriverException - { - if (!isEmpty) - { - Node curr = root; // 0 is always the root. - Key keyFound; - byte[] typesAux = types; - SQLValue[] keys = key.keys; - Key[] currKeys; - int[] children; - PlainDB plainDB = table.db; - int pos, - nodeCounter = nodeCount, - firstChild, - size, - count = 0; - int[] vector = plainDB.driver.nodes; - - while (true) - { - keyFound = (currKeys = curr.keys)[pos = curr.findIn(key, false)]; // juliana@201_3 - firstChild = (children = curr.children)[0]; - - // juliana@284_2: solved a possible insertion of a duplicate value in a PK. - if (pos < (size = curr.size) && Utils.arrayValueCompareTo(keys, keyFound.keys, typesAux, plainDB) == 0) - { - if (markBits == null) // Only checks primary key violation. - if (keyFound.record != Key.NO_VALUE) - throw new PrimaryKeyViolationException(LitebaseMessage.getMessage(LitebaseMessage.ERR_STATEMENT_CREATE_DUPLICATED_PK) - + table.name); - do - pos--; - while (pos >= 0 && Utils.arrayValueCompareTo(keys, currKeys[pos].keys, typesAux, plainDB) == 0); - while (++pos < size && Utils.arrayValueCompareTo(keys, currKeys[pos].keys, typesAux, plainDB) == 0) - { - if (markBits != null) - markBits.onKey(currKeys[pos]); - if (firstChild != Node.LEAF) - vector[count++] = children[pos]; - } - } - - if (firstChild != Node.LEAF) - vector[count++] = children[pos]; - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - - if (count > 0) - curr = loadNode(vector[--count]); - else - break; - } - } - } - - /** - * Climbs on the nodes that are greater or equal than the current one. - * - * @param node The node to be compared with. - * @param start The first key of the node to be searched. - * @param markBits The rows which will be returned to the result set. - * @param stop Indicates when the climb process can be finished. - * @return If it has to stop the climbing process or not. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private boolean climbGreaterOrEqual(Node node, int start, MarkBits markBits, boolean stop) throws IOException, InvalidDateException - { - int size = node.size; - Key[] keys = node.keys; - int[] children = node.children; - if (start >= 0) - stop = !markBits.onKey(keys[start]); - if (children[0] == Node.LEAF) - while (!stop && ++start < size) - stop = !markBits.onKey(keys[start]); - else - { - Node curr, - loaded; - - if (nodesArrayCount > 0) - curr = nodes[--nodesArrayCount]; - else - curr = new Node(node.index); - - while (!stop && ++start <= size) - { - if ((loaded = getLoadedNode(children[start])) == null) - { - (loaded = curr).idx = children[start]; - curr.load(); - } - stop = climbGreaterOrEqual(loaded, -1, markBits, stop); - if (start < size && !stop) - stop = !markBits.onKey(keys[start]); - } - nodes[nodesArrayCount++] = curr; - } - return stop; - } - - /** - * Starts from the root to find the left key, then climbs from it until the end. - * - * @param markBits The bitmap that represents all the table rows. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws DriverException If the index is corrupted. - */ - void getGreaterOrEqual(MarkBits markBits) throws IOException, InvalidDateException, DriverException - { - if (!isEmpty) - { - int pos, - nodeCounter = nodeCount, - r, - count = 0; - PlainDB plainDB = table.db; - int[] ancestors = plainDB.driver.nodes; - Node curr = root; // Starts from the root. - Key left = markBits.leftKey; - SQLValue[] leftKeys = left.keys; - Key[] currKeys; - int[] children; - byte[] typesAux = types; - - while (true) - { - children = curr.children; - currKeys = curr.keys; - - if ((pos = curr.findIn(left, false)) < curr.size) // juliana@201_3 - { - // Compares left keys with curr keys. - // If this value is above or equal to the one being looked for, stores it. - while (--pos >= 0 && Utils.arrayValueCompareTo(leftKeys, currKeys[pos].keys, typesAux, plainDB) == 0); - if ((r = Utils.arrayValueCompareTo(leftKeys, currKeys[++pos].keys, typesAux, plainDB)) <= 0) - { - ancestors[count++] = curr.idx; - ancestors[count++] = pos; - } - else if (r >= 0) // left >= curr.keys[pos] ? - break; - } - if (children[0] == Node.LEAF) - break; - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - curr = loadNode(children[pos]); - } - if (count > 0) - { - boolean stop; - - while (count > 0) - { - stop = false; - pos = ancestors[--count]; - if ((stop = climbGreaterOrEqual(curr = loadNode(ancestors[--count]), pos, markBits, stop))) - break; - } - } - } - } - - /** - * Splits the overflown node of this B-Tree. The stack ancestors contains all ancestors of the node, together with the known insertion position in - * each of these ancestors. - * - * @param curr The current node. - * @param count The number of elements in the ancestors array. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private void splitNode(Node curr, int count) throws IOException, InvalidDateException - { - int left, - right, - ins; - Key keyFound, - keyAux = tempKey; - Node rootAux = root; - int[] ancestors = table.db.driver.nodes; // juliana@224_2: improved memory usage on BlackBerry. - - // guich@110_3: curr.size * 3/4 - note that medPos never changes, because the node is always split when the same size is reached. - // juliana@283_1: solved a bug which would buid corrupted indices when creating or recreating them. - int medPos = curr.index.isOrdered? (curr.size - 2) : (curr.size / 2); - - while (true) - { - keyFound = curr.keys[medPos]; - keyAux.set(keyFound.keys); - keyAux.record = keyFound.record; - - // Right sibling - must be the first one to save! - right = curr.save(true, medPos + 1, curr.size); - - if (curr.idx == 0) // Is it the root? - { - left = curr.save(true, 0, medPos); // Left sibling. - rootAux.set(keyAux, left, right); // Replaces the root record. - rootAux.save(false, 0, rootAux.size); - break; - } - else // guich@110_4: reuses this node; cut it at medPos. - { - left = curr.idx; - curr.save(false, 0, curr.size = medPos); - ins = 0; - if (count >= 0) // Parent insert position. - { - curr = loadNode(ancestors[--count]); // Loads the parent. - ins = ancestors[--count]; - } - - curr.insert(keyAux, left, right, ins); - if (curr.size < btreeMaxNodes) // Parent has not overflown? - break; - } - } - } - - /** - * Closes the index files. - * - * @throws IOException If an internal method throws it. - */ - void close() throws IOException - { - fnodes.finalPos = nodeCount * nodeRecSize; // Calculated the used space; the file will have no zeros at the end. - fnodes.close(); - } - - /** - * Empties the index files, since the rows were deleted. - * - * @throws IOException If an internal method throws it. - */ - void deleteAllRows() throws IOException - { - // It is faster truncating a file than re-creating it again. - NormalFile fnodesAux = fnodes; - Node[] cacheAux = cache; - Node[] firstLevelAux = firstLevel; - - fnodesAux.growTo(0); - fnodesAux.finalPos = fnodesAux.pos = fnodesAux.size = 0; - fnodesAux.cacheIsDirty = false; - - isEmpty = true; - int i = INDEX_CACHE_SIZE; - while (--i >= 0) // Erases the cache. - if (cacheAux[i] != null) - cacheAux[i].idx = -1; - - i = btreeMaxNodes; - while (--i >= 0) // Erases the first level nodes. - if (firstLevelAux[i] != null) - firstLevelAux[i].idx = -1; - - cacheI = nodeCount = 0; // juliana@220_6: The node count should be reseted when recreating the indices. - } - - /** - * Delays the write to disk, caching them at memory. - * - * @param delayed Indicates if the writing process is to be done later or not. - * @throws IOException If an internal method throws it. - */ - void setWriteDelayed(boolean delayed) throws IOException - { - root.setWriteDelayed(delayed); // Commits pending keys. - - // juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - // Commits the pending first level nodes. - int i = btreeMaxNodes; - Node[] nodes = firstLevel; - while (--i >= 0) - if (nodes[i] != null) - nodes[i].setWriteDelayed(delayed); - - // Commits the pending cache nodes. - i = INDEX_CACHE_SIZE; - nodes = cache; - while (--i >= 0) - if (nodes[i] != null) - nodes[i].setWriteDelayed(delayed); - - if (!delayed) // Shrinks the values. - fnodes.growTo(nodeCount * nodeRecSize); - isWriteDelayed = delayed; - } - - /** - * Adds a key to an index. - * - * @param values The key to be inserted. - * @param record The record of the key in the table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void indexAddKey(SQLValue[] values, int record) throws IOException, InvalidDateException - { - Key keyAux = tempKey; - Node rootAux = root; - - keyAux.set(values); // Sets the key. - - // Inserts the key. - boolean splitting = false; - if (isEmpty) - { - keyAux.record = record; - rootAux.set(keyAux, Node.LEAF, Node.LEAF); - rootAux.save(true, 0, 1); - isEmpty = false; - } - else - { - Node curr = rootAux; - Key keyFound; - byte[] typesAux = types; - int[] children; - SQLValue[] keys = keyAux.keys; - Key[] currKeys; - PlainDB plainDB = table.db; - int nodeCountAux = nodeCount, - nodeCounter = nodeCountAux, - maxSize = btreeMaxNodes - 1, - pos, - size, - count = 0; - int[] ancestors = plainDB.driver.nodes; // juliana@224_2: improved memory usage on BlackBerry. - - while (true) - { - keyFound = (currKeys = curr.keys)[pos = curr.findIn(keyAux, true)]; // juliana@201_3 - children = curr.children; - - if (pos < (size = curr.size) && Utils.arrayValueCompareTo(keys, keyFound.keys, typesAux, plainDB) == 0) - { - // juliana@281_1: corrected a possible index corruption. - while (pos >= 0 && Utils.arrayValueCompareTo(keys, (keyFound = currKeys[pos]).keys, typesAux, plainDB) == 0 - && (keyFound.record >= record || keyFound.record == Key.NO_VALUE)) - pos--; - while (++pos < size && Utils.arrayValueCompareTo(keys, (keyFound = currKeys[pos]).keys, typesAux, plainDB) == 0 - && (keyFound.record < record || keyFound.record == Key.NO_VALUE)); - } - - if (children[0] == Node.LEAF) - { - // If the node will becomes full, the insert is done again, this time keeping track of the ancestors. Note: with k = 50 and 200000 - // values, there are about 1.1 million useless pushes without this redundant insert. - if (!splitting && curr.size == maxSize) - { - splitting = true; - curr = rootAux; - count = 0; - nodeCounter = nodeCountAux; - } - else - { - keyAux.record = record; - curr.insert(keyAux, Node.LEAF, Node.LEAF, pos); - curr.saveDirtyKey(pos); - if (splitting) // Curr has overflown. - splitNode(curr, count); - break; - } - } - else - { - if (splitting) - { - ancestors[count++] = pos; - ancestors[count++] = curr.idx; - } - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - curr = loadNode(children[pos]); - } - } - } - } - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - /** - * Finds the minimum value of an index in a range. - * - * @param sqlValue The minimum value inside the given range to be returned. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - */ - void findMinValue(SQLValue sqlValue, IntVector bitMap) throws IOException, InvalidDateException - { - Node curr; - Key[] currKeys; - Key currKey; - int size, - i, - nodeCounter = nodeCount + 1, - count = 1; - int[] vector = table.db.driver.nodes; - int[] children; - - // juliana@224_2: improved memory usage on BlackBerry. - - // Recursion using a stack. The array sole element is 0. - vector[0] = 0; - while (count > 0) - { - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - curr = loadNode(vector[--count]); - - // Searches for the smallest key of the node marked in the result set or is not deleted. - size = curr.size; - i = -1; - currKeys = curr.keys; - children = curr.children; - - while (++i < size) - if ((currKey = currKeys[i]).record != Key.NO_VALUE && (bitMap == null || bitMap.isBitSet(currKey.record))) - { - currKey.keys[0].cloneSQLValue(sqlValue); - count = 0; // juliana@284_3: solved a possible wrong result in MAX() and MIN() if the column searched had an index. - break; - } - - // Now searches the children nodes whose keys are smaller than the one marked or all of them if no one is marked. - i++; - if (children[0] != Node.LEAF) - while (--i >= 0) - vector[count++] = children[i]; - } - - if (sqlValue.isNull) // No record found. - return; - - loadString(sqlValue); - } - - /** - * Finds the maximum value of an index in a range. - * - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param sqlValue The maximum value inside the given range to be returned. - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - */ - void findMaxValue(SQLValue sqlValue, IntVector bitMap) throws IOException, InvalidDateException - { - Node curr; - Key[] currKeys; - Key currKey; - int size, - i, - count = 1, - nodeCounter = nodeCount + 1; - int[] vector = table.db.driver.nodes; - int[] children; - - // juliana@224_2: improved memory usage on BlackBerry. - - // Recursion using a stack. The array sole element is 0. - vector[0] = 0; - while (count > 0) - { - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - curr = loadNode(vector[--count]); - - // Searches for the greatest key of the node marked in the result set or is not deleted. - i = size = curr.size; - currKeys = curr.keys; - children = curr.children; - - while (--i >= 0) - if ((currKey = currKeys[i]).record != Key.NO_VALUE && (bitMap == null || bitMap.isBitSet(currKey.record))) - { - currKey.keys[0].cloneSQLValue(sqlValue); - count = 0; // juliana@284_3: solved a possible wrong result in MAX() and MIN() if the column searched had an index. - break; - } - - // Now searches the children nodes whose keys are greater than the one marked or all of them if no one is marked. - if (children[0] != Node.LEAF) - while (++i <= size) - vector[count++] = children[i]; - } - - if (sqlValue.isNull) // No record found. - return; - loadString(sqlValue); - } - - /** - * Loads a string from the table if needed. - * - * @param sqlValue The record structure which will hold (holds) the string. - * @throws IOException If an internal method throws it. - */ - private void loadString(SQLValue sqlValue) throws IOException - { - // If the type is string and the value is not loaded, loads it. - if (types[0] == SQLElement.CHARS || types[0] == SQLElement.CHARS_NOCASE) - { - sqlValue.asLong = Utils.subStringHashCode(table.name, 5); - if (sqlValue.asString == null) - sqlValue.asString = table.db.loadString(); - } - } - - /** - * Returns a node already loaded or loads it if there is empty space in the cache node to avoid loading already loaded nodes. - * - * @param idx The node index. - * @return The loaded node, a new cache node with the requested node loaded, a first level node, or null if it is not already loaded - * and its cache is full. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private Node getLoadedNode(int idx) throws IOException, InvalidDateException - { - Node node; - - // juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - // Tries to find the node in the nodes of the first level. - if (idx <= btreeMaxNodes) - { - if ((node = firstLevel[idx - 1]) == null) - { - (node = firstLevel[idx - 1] = new Node(this)).idx = idx; - node.load(); - } - else if (node.idx == -1) - { - node.idx = idx; - node.load(); - } - - return node; - } - - // Tries to get an already loaded node if it is a node from a deeper level. - Node[] cacheAux = cache; - - int i = -1; - while (++i < INDEX_CACHE_SIZE && cacheAux[i] != null) - if (cacheAux[i].idx == idx) - return cacheAux[cacheI = i]; - - if (i < INDEX_CACHE_SIZE) // Loads the node if there is enough space in the node cache. - { - (node = cacheAux[cacheI = i] = new Node(this)).idx = idx; - node.load(); - return node; - } - - return null; - } - - // juliana@230_29: order by and group by now use indices on simple queries. - /** - * Sorts the records of a table into a temporary table using an index in the ascending order. - * - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @param clause The select clause of the query. - * @throws DriverException If the index is corrupted. - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - */ - void sortRecordsAsc(IntVector bitMap, Table tempTable, SQLValue[] record, short[] columnIndexes, SQLSelectClause clause) - throws DriverException, InvalidDateException, IOException - { - int size, - i, - valRec, - node, - nodeCounter = nodeCount + 1, - count = 1; - Node curr; - int[] valRecs = new int[nodeCounter]; - int[] nodes = table.db.driver.nodes; - Key[] keys; - int[] children; - - // Recursion using a stack. The nodes array sole element is 0. - valRecs[0] = Key.NO_VALUE; - nodes[0] = 0; - while (count > 0) - { - // Gets the key and child node. - valRec = valRecs[--count]; - node = nodes[count]; - - // Loads a node if it is not a leaf node. - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - - size = (curr = loadNode(node)).size; - children = curr.children; - keys = curr.keys; - - if (children[0] == Node.LEAF) // If the node do not have children, just process its keys in the ascending order. - { - i = -1; - while (++i < size) - writeKey(keys[i].record, bitMap, tempTable, record, columnIndexes, clause); - writeKey(valRec, bitMap, tempTable, record, columnIndexes, clause); - } - else // If not, push its key and process its children in the ascending order. - { - if (size > 0) - { - valRecs[count] = valRec; - nodes[count++] = children[size]; - } - while (--size >= 0) - { - valRecs[count] = keys[size].record; - nodes[count++] = children[size]; - } - } - } - } - - /** - * Sorts the records of a table into a temporary table using an index in the descending order. - * - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @param clause The select clause of the query. - * @throws DriverException If the index is corrupted. - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - */ - void sortRecordsDesc(IntVector bitMap, Table tempTable, SQLValue[] record, short[] columnIndexes, SQLSelectClause clause) - throws DriverException, InvalidDateException, IOException - { - int size, - i, - valRec, - node, - nodeCounter = nodeCount + 1, - count = 1; - Node curr; - int[] valRecs = new int[nodeCounter]; - int[] nodes = table.db.driver.nodes; - Key[] keys; - int[] children; - - // Recursion using a stack. - // Recursion using a stack. The nodes array sole element is 0. - valRecs[0] = Key.NO_VALUE; - nodes[0] = 0; - while (count > 0) - { - // Gets the key and child node. - valRec = valRecs[--count]; - node = nodes[count]; - - // Loads a node if it is not a leaf node. - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_LOAD_NODE)); - - size = (curr = loadNode(node)).size; - children = curr.children; - keys = curr.keys; - - if (children[0] == Node.LEAF) // If the node do not have children, just process its keys in the descending order. - { - writeKey(valRec, bitMap, tempTable, record, columnIndexes, clause); - i = size; - while (--i >= 0) - writeKey(keys[i].record, bitMap, tempTable, record, columnIndexes, clause); - } - else // If not, process its children in the descending order and then push its key. - { - i = -1; - while (++i < size) - { - valRecs[count] = keys[i].record; - nodes[count++] = children[i]; - } - if (size > 0) - { - valRecs[count] = valRec; - nodes[count++] = children[size]; - } - } - } - } - - /** - * Writes all the records with a specific key in the temporary table that satisfy the query where clause. - * - * @param valRec The record index. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @param clause The select clause of the query. - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - */ - private void writeKey(int valRec, IntVector bitMap, Table tempTable, SQLValue[] record, short[] columnIndexes, SQLSelectClause clause) - throws IOException, InvalidDateException - { - if (valRec != Key.NO_VALUE && (bitMap == null || bitMap.isBitSet(valRec))) - { - Table tableAux = table; - byte[] tempNulls = tempTable.columnNulls[0]; - byte[] origNulls = tableAux.columnNulls[0]; - short[] offsets = tableAux.columnOffsets; - byte[] types = tableAux.columnTypes; - int i = tempTable.columnCount, - colIndex; - boolean isNull; - - tableAux.db.read(valRec); // Reads the record. - tableAux.readNullBytesOfRecord(0, false, 0); // Reads the bytes of the nulls. - - while (--i >= 0) // Reads the fields for the temporary table. - { - colIndex = columnIndexes[i]; - if (!(isNull = (origNulls[colIndex >> 3] & (1 << (colIndex & 7))) != 0)) - tableAux.readValue(record[i], offsets[colIndex], types[colIndex], false, true); - - Utils.setBit(tempNulls, i, isNull); // Sets the null values for tempTable. - } - tempTable.writeRSRecord(record); // Writes the temporary table record. - } - } -} diff --git a/LitebaseSDK/src/java/litebase/Key.java b/LitebaseSDK/src/java/litebase/Key.java deleted file mode 100644 index a2f5b7bf3f..0000000000 --- a/LitebaseSDK/src/java/litebase/Key.java +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.util.InvalidDateException; - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * This class represents the key of a record. It may be any of the SQL types defined here. - */ -@Deprecated -class Key -{ - /** - * The size of valRec, an int. - */ - static final int VALREC_SIZE = 4; - - /** - * Represents a key that has no values attached to it. - */ - static final int NO_VALUE = 0xFFFFFFFF; // juliana@230_21 - - /** - * The values stored in the key. - */ - SQLValue[] keys; - - /** - * The record index or NO_VALUE. - */ - int record; - - /** - * The index that has this key. - */ - Index index; - - /** - * Creates a Key object. - * - * @param anIndex The index which has the new key. - */ - Key(Index anIndex) - { - // Initializes the key object. - index = anIndex; - keys = SQLValue.newSQLValues(index.types.length); - record = NO_VALUE; - } - - /** - * Sets a key of an index. - * - * @param key The values used to set the index key. - */ - void set(SQLValue[] key) - { - int i = index.types.length; - byte[] types = index.types; - SQLValue[] keysAux = keys; - - while (--i >= 0) - { - try - { - switch (types[i]) - { - case SQLElement.DATETIME: // DATETIME. - keysAux[i].asShort = key[i].asShort; - case SQLElement.CHARS: // CHARS and VARCHAR. - case SQLElement.CHARS_NOCASE: // CHARS NOCASE and VARCHAR NOCASE. - case SQLElement.DATE: // DATE. - keysAux[i].asString = key[i].asString; // juliana@230_3 - case SQLElement.INT: // INT. - keysAux[i].asInt = key[i].asInt; - break; - case SQLElement.SHORT: // SHORT. - keysAux[i].asShort = key[i].asShort; - break; - case SQLElement.LONG: // LONG. - keysAux[i].asLong = key[i].asLong; - break; - case SQLElement.FLOAT: // FLOAT. - case SQLElement.DOUBLE: // DOUBLE. - keysAux[i].asDouble = key[i].asDouble; - // Blobs can't be used in indices. - } - } - catch (NullPointerException exception) {} // juliana@251_12: removed a possible NPE when using indices with null. - } - record = NO_VALUE; // The record key is not stored yet. - } - - /** - * Loads a key. - * - * @param ds The data stream where the record to be read to find the key value stored. - * @throws IOException If an internal method throws it. - */ - void load(DataStreamLB ds) throws IOException, InvalidDateException // juliana@253_8 - { - Index indexAux = index; - byte[] types = indexAux.types; - int n = types.length, - i = -1; - int[] colSizes = indexAux.colSizes; - PlainDB db = indexAux.table.db; - SQLValue key; - - while (++i < n) - { - key = keys[i]; - - // String keys are not stored in the indices. Only their pointer is stored. - if (colSizes[i] > 0) - { - // If the position is the same, the string is already loaded. - int pos = ds.readInt(); - if (pos != key.asInt) - { - key.asString = null; - key.asInt = pos; - } - } - else - // Must pass true to isTemporary so that the method does not think that the number is a rowid. - // If the value read is null, some bytes must be skipped in the stream. - // Note: since we're writing only primitive types, we can use any PlainDB available. - // juliana@220_3 // juliana@230_14 - ds.skipBytes(colSizes[i] - db.readValue(key, 0, types[i], ds, true, false, false)); - } - record = ds.readInt(); // Reads the number that represents the record. - } - - /** - * Saves a key. - * - * @param ds The data stream where to write the record. - * @throws IOException If an internal method throws it. - */ - void save(DataStreamLB ds) throws IOException // juliana@253_8 - { - Index indexAux = index; - byte[] types = indexAux.types; - int n = types.length, - i = -1; - int[] colSizes = indexAux.colSizes; - SQLValue[] keysAux = keys; - - while (++i < n) - { - if (colSizes[i] > 0) - ds.writeInt(keysAux[i].asInt); // Saves only the string position in the .dbo. - else - // If the key is not a string, stores its value in the index file. - // Note: since primitive types are being written, it is possible to use any PlainDB available. - // juliana@220_3 - index.table.db.writeValue(types[i], keysAux[i], ds, true, true, 0, 0, false); - } - ds.writeInt(record); // Writes the number that represents the record. - } -} diff --git a/LitebaseSDK/src/java/litebase/LitebaseConnection.java b/LitebaseSDK/src/java/litebase/LitebaseConnection.java deleted file mode 100644 index 07e270b6e2..0000000000 --- a/LitebaseSDK/src/java/litebase/LitebaseConnection.java +++ /dev/null @@ -1,2787 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * This class is the one used to issue SQL commands. Read Litebase Companion chapters for more information. - */ -@Deprecated -public class LitebaseConnection -{ - /** - * English language. - */ - public static final int LANGUAGE_EN = 1; - - // guich@223_10: the Litebase version is not declared as final anymore, otherwise the compiler replaces the constant by the value. - /** - * Portuguese language. - */ - public static final int LANGUAGE_PT = 2; - - /** - * The string corresponding to the current Litebase version. - */ - public static String versionStr = "2.8.5"; - - /** - * The integer corresponding to the current Litebase version. - */ - public static int version = 285; - - /** - * Current build number. - */ - public static int buildNumber = 000; - - /** - * The key which identifies one Litebase connection instance. - */ - private int key; - - /** - * A hash table containing the connections to Litebase in use. - */ - static Hashtable htDrivers = new Hashtable(10); - - /** - * The logger. - */ - public static Logger logger; - - // juliana@211_1: language is now a public field. It must be accessed directly. - /** - * The language of the Litebase messages. - */ - public static int language = LANGUAGE_EN; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - /** - * Indicates if only changes during Litebase operation must be logged or not. - */ - public static boolean logOnlyChanges; - - /** - * Given the table name, returns the Table object. - */ - Hashtable htTables; - - /** - * A hash table of prepared statements. - */ - Hashtable htPS = new Hashtable(30); // guich@201_3 // juliana@253_20: added PreparedStatement.close(). - - /** - * The creator id for the tables managed by Litebase. - */ - String appCrid; - - /** - * The source path, where the tables will be stored. - */ - String sourcePath; - - /** - * Indicates that the object can be collected. - */ - boolean dontFinalize; - - /** - * Indicates if the tables of this connection use ascii or unicode strings. - */ - private boolean isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - - /** - * Indicates if the tables of this connection use cryptography. - */ - private boolean useCrypto; // juliana@253_8: now Litebase supports weak cryptography. - - // juliana@224_2: improved memory usage on BlackBerry. - /** - * A temporary date object. - */ - Date tempDate = new Date(); - - /** - * A temporary time object. - */ - static Time tempTime = new Time(); - - /** - * An auxiliary value. - */ - SQLValue sqlv = new SQLValue(); - - // juliana@253_6: the maximum number of keys of a index was duplicated. - /** - * An array used for nodes indices. - */ - int[] nodes = new int[Node.MAX_IDX]; - - /** - * A temporary buffer for strings. - */ - StringBuffer sBuffer = new StringBuffer(); - - /** - * An auxiliary single value for index manipulation. - */ - SQLValue[] oneValue = new SQLValue[1]; - - // juliana@253_5: removed .idr files from all indices and changed its format. - // juliana@230_13: removed some possible strange behaviours when using threads. - /** - * A byte for saving table meta data. - */ - byte[] oneByte = new byte[1]; // juliana@226_4 - - /** - * A buffer used for reading ascii strings in PlainDB.readValue(). - */ - byte[] buffer = new byte[1]; - - /** - * A buffer used for reading unicode strings in PlainDB.readValue(). - */ - char[] valueAsChars = new char[1]; - - /** - * The lexical analizer. - */ - LitebaseLex lexer = new LitebaseLex(); - - static - { - if (Settings.deviceId == null) // juliana@lb201_30: fills Settings if its a headless application. - new totalcross.Launcher().fillSettings(); - } - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - private LitebaseConnection() {} - - // juliana@201_26: created a default getInstance() which creates a new Litebase connection with the current application id. - /** - * Creates a Litebase connection for the default creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @return A Litebase instance. - */ - public static LitebaseConnection getInstance() - { - return getInstance(Settings.applicationId, null); - } - - /** - * Creates a Litebase connection for the given creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param appCrid The creator id, which may (or not) be the same one of the current application and MUST be 4 characters long. - * @return A Litebase instance. - */ - public static LitebaseConnection getInstance(String appCrid) - { - return getInstance(appCrid, null); - } - - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Creates a LitebaseConnection for the given creator id and with the given connection param list. This method avoids the creation of more than - * one instance with the same creator id and parameters, which would lead to performance and memory problems. - * - * @param appCrid The creator id, which may be the same one of the current application and MUST be 4 characters long. - * @param params Only the folder where it is desired to store the tables, null, if it is desired to use the current data - * path, or chars_type = chars_format; path = source_path[;crypto] , where chars_format can be ascii or - * unicode, source_path is the folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is passed as a parameter, unicode is used and there is no - * cryptography. Notice that path must be absolute, not relative. - *

Note that databases belonging to multiple applications can be stored in the same path, since all tables are prefixed by the application's - * creator id. - *

Also notice that to store Litebase files on card on Pocket PC, just set the second parameter to the correct directory path. - *

It is not recommended to create the databases directly on the PDA. Memory cards are FIVE TIMES SLOWER than the main memory, so it will take - * a long time to create the tables. Even if the NVFS volume is used, it can be very slow. It is better to create the tables on the desktop, and - * copy everything to the memory card or to the NVFS volume. - *

Due to the slowness of a memory card and the NVFS volume, all queries will be stored in the main memory; only tables and indexes will be - * stored on the card or on the NVFS volume. - *

An exception will be raised if tables created with an ascii kind of connection are oppened with an unicode connection and vice-versa. - * @return A Litebase instance. - * @throws DriverException If an IOException occurs or an application id with more or less than four characters is specified. - */ - public static LitebaseConnection getInstance(String appCrid, String params) throws DriverException - { - try - { - if (appCrid.length() != 4) // The application id must have 4 characters. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_CRID)); - - int key = (appCrid + '|' + params + '|' + Thread.currentThread()).hashCode(); // The key which identifies the Litebase connection. - - LitebaseConnection conn = (LitebaseConnection)htDrivers.get(key); // Tries to get a connection with the same key. - - if (conn == null) // If there is no connections with this key, creates a new one. - { - conn = new LitebaseConnection(); - if (logger != null) - synchronized (logger) - { - conn.sBuffer.setLength(0); - logger.logInfo(conn.sBuffer.append("new LitebaseConnection(").append(appCrid).append(",").append(params).append(")")); - } - - // juliana@250_4: now getInstance() can receive only the parameter chars_type = ... - // juliana@210_2: now Litebase supports tables with ascii strings. - String path = null; - if (params != null) - { - String[] paramsSeparated = Convert.tokenizeString(params, ';'); // Separates the parameters. - String tempParam = null; - int len = paramsSeparated.length; - - while (--len >= 0) // The parameters order does not matter. - { - tempParam = paramsSeparated[len].trim(); - if (tempParam.startsWith("chars_type")) // Chars type param. - conn.isAscii = tempParam.indexOf("ascii") != -1; - else if (tempParam.startsWith("path")) // Path param. - path = tempParam.substring(tempParam.indexOf('=') + 1).trim(); - else if (tempParam.startsWith("crypto")) // Cryptography param. - conn.useCrypto = true; - else if (paramsSeparated.length == 1) - path = params; // Things do not change if there is only one parameter that is the path. - else // Invalid parameter // juliana@253_11: now a DriverException will be throw if an incorrect parameter is passed in LitebaseConnection.getInstance(). - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER) + "tempParam"); - } - } - - // juliana@214_1: relative paths can't be used with Litebase. - validatePath(path = conn.sourcePath = (path != null)? path : Settings.dataPath != null && Settings.dataPath.length() != 0? Settings.dataPath : Settings.appPath); - - // If the source folder does not exist, it is created. This creation is recursive. - File file = new File(path); - if (path.length() > 0 && !file.exists()) - file.createDir(); - - if (!path.endsWith("\\") && !path.endsWith("/")) // Appends a "/" if the datapath does not end with "\\" or "/". - conn.sourcePath = path + '/'; - - conn.appCrid = appCrid; - conn.htTables = new Hashtable(10); - conn.key = key; - conn.lexer.nameToken = conn.sBuffer; - - synchronized(htDrivers) // juliana@230_13: removed some possible strange behaviours when using threads. - { - htDrivers.put(key, conn); - } - } - return conn; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the path where the tables created/opened by this connection are stored. - * - * @return A string representing the path. - * @throws IllegalStateException If the driver is closed. - */ - public String getSourcePath() throws IllegalStateException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - return sourcePath; - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to execute a create table or create index SQL commands. - * - *

Examples: - *

    - *
  • driver.execute("create table PERSON (NAME CHAR(30), SALARY DOUBLE, AGE INT, EMAIL CHAR(50))"); - *
  • driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - *
- * - *

When creating an index, its name is ignored but must be given. The index can be created after data was added to the table. - * - * @param sql The SQL creation command. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - * @throws SQLParseException If the table name or a default string is too big, there is an invalid default value, or an unknown (on a create - * table) or repeated column name, or an InvalidDateException or an InvalidNumberException occurs. - * @throws AlreadyCreatedException If the table or index is already created. - */ - public void execute(String sql) throws IllegalStateException, DriverException, SQLParseException, AlreadyCreatedException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (logger != null) - synchronized (logger) - { - logger.log(Logger.INFO, sql, false); - } - - try - { - int i; - LitebaseParser parser = new LitebaseParser(); - parser.tableList = new SQLResultSetTable[1]; - String sqlAux = sql.toLowerCase().trim(); - - // juliana@270_18: Solved NPE being thrown instead of a SQLParseException when an insert is passed to LitebaseConnection.execute(). - if (sqlAux.startsWith("create ")) - { - if (sqlAux.startsWith("create table")) - parser.fieldList = new SQLFieldDefinition[SQLElement.MAX_NUM_COLUMNS]; - parser.fieldNames = new String[SQLElement.MAX_NUM_COLUMNS]; - } - else - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED)); - - // juliana@224_2: improved memory usage on BlackBerry. - LitebaseParser.parser(sql, parser, lexer); // Does de parsing. - - if (parser.command == SQLElement.CMD_CREATE_TABLE) // CREATE TABLE - { - String tableName = parser.tableList[0].tableName; - - // Verifies the length of the table name. - if (tableName.length() > SQLElement.MAX_TABLE_NAME_LENGTH_AS_PLAIN_FILE) // rnovais@570_114: The table name can't be infinite. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_MAX_TABLE_NAME_LENGTH)); - - if (exists(tableName)) // guich@105: verifies if it is already created. - throw new AlreadyCreatedException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_ALREADY_CREATED) + tableName); - - // Counts the number of fields. - int count = parser.fieldListSize + 1; // fieldListSize + rowid - - // Now gets the columns. - String[] names = new String[count]; - int[] hashes = new int[count]; - byte[] types = new byte[count]; - int[] sizes = new int[count]; - int primaryKeyCol = Utils.NO_PRIMARY_KEY, - composedPK = Utils.NO_PRIMARY_KEY; - SQLValue[] defaultValues = new SQLValue[count]; - byte[] columnAttrs = new byte[count]; - SQLFieldDefinition field; - Date tempDateAux = tempDate; // juliana@224_2: improved memory usage on BlackBerry. - String defaultValue; - - // Creates column 0 (rowid). - names[0] = "rowid"; - types[0] = SQLElement.INT; - hashes[0] = 108705909; - - i = count; - while (--i > 0) // Creates the other columns. - { - field = parser.fieldList[i - 1]; - hashes[i] = (names[i] = field.fieldName).hashCode(); - types[i] = (byte)field.fieldType; - sizes[i] = field.fieldSize; - - if (field.isPrimaryKey) // Checks if there is a primary key definition. - primaryKeyCol = i; // Only one primary key can be defined per table: this is verified during the parsing. - - if ((defaultValue = field.defaultValue) != null) // Default values: default null has no effect. This is handled by the parser. - { - defaultValues[i] = new SQLValue(); - columnAttrs[i] |= Utils.ATTR_COLUMN_HAS_DEFAULT; // Sets the default bit. - - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than - // the type range. - switch (field.fieldType) - { - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - if (defaultValue.length() > sizes[i]) // The default value size can't be larger than the size of the field definition. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER)); - defaultValues[i].asString = defaultValue; - break; - case SQLElement.SHORT: - defaultValues[i].asShort = Convert.toShort(defaultValue); - break; - case SQLElement.INT: - defaultValues[i].asInt = Convert.toInt(defaultValue); - break; - case SQLElement.LONG: - defaultValues[i].asLong = Convert.toLong(defaultValue); - break; - case SQLElement.FLOAT: - defaultValues[i].asDouble = Utils.toFloat(defaultValue); - break; - case SQLElement.DOUBLE: - defaultValues[i].asDouble = Convert.toDouble(defaultValue); - break; - case SQLElement.DATE: // juliana@224_2: improved memory usage on BlackBerry. - defaultValues[i].asInt = tempDateAux.set(defaultValue, Settings.DATE_YMD); - break; - case SQLElement.DATETIME: // juliana@224_2: improved memory usage on BlackBerry. - int pos = defaultValue.lastIndexOf(' '); - if (pos == -1) // There is no time here. - { - defaultValues[i].asInt = tempDateAux.set(defaultValue, Settings.DATE_YMD); - defaultValues[i].asShort = 0; - } - else - { - defaultValues[i].asInt = tempDateAux.set(defaultValue.substring(0, pos), Settings.DATE_YMD); - defaultValues[i].asShort = Utils.testAndPrepareTime(defaultValue.substring(pos + 1).trim()); - } - } - } - - if (field.isNotNull) // Sets the 'not null' bit. - columnAttrs[i] |= Utils.ATTR_COLUMN_IS_NOT_NULL; - } - - // Gets the composed primary keys. - byte[] composedPKCols = null; - int composedPKSize = parser.fieldNamesSize; - String[] composedPK_Fields = parser.fieldNames; - if (composedPKSize > 0) - { - composedPKCols = new byte[composedPKSize]; - int j, - pos = -1; - i = -1; - while (++i < composedPKSize) - { - pos = -1; - j = count; - - while (--j >= 0) // Checks if the name of a table column exist. - if (composedPK_Fields[i].equals(names[j])) - { - pos = j; - break; - } - if (pos == -1) // Column not found. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + composedPK_Fields[i]); - - - if (types[pos] == SQLElement.BLOB) // A blob can't be in a composed PK. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_PRIMARY_KEY)); - - j = -1; - while (++j < i) // Verifies if there's a duplicate definition. - if (composedPKCols[j] == pos) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATED_COLUMN_NAME) + composedPK_Fields[i]); - - composedPKCols[i] = (byte)pos; - } - if (composedPKSize == 1) - { - primaryKeyCol = pos; - composedPKCols = null; - } - else - composedPK = 0; - } - driverCreateTable(tableName, names, hashes, types, sizes, columnAttrs, defaultValues, primaryKeyCol, composedPK, composedPKCols); - } - else if (parser.command == SQLElement.CMD_CREATE_INDEX) - { - // indexTableName ignored - formed internally. - String tableName = parser.tableList[0].tableName; - String[] indexNames = new String[parser.fieldNamesSize]; - Hashtable names = new Hashtable(10); - String indexName; - - // juliana@225_8: it was possible to create a composed index with duplicated column names. - i = parser.fieldNamesSize; - while (--i >= 0) - if (names.get(indexName = indexNames[i] = parser.fieldNames[i]) == null) - names.put(indexName, indexName); - else - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATED_COLUMN_NAME)); - - driverCreateIndex(tableName, indexNames, null, false); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to execute updates in a table (insert, delete, update, alter table, drop). E.g.: - * - *

driver.executeUpdate("drop table person"); will drop also the indices. - *

driver.executeUpdate("drop index * on person"); will drop all indices but not the primary key index. - *

driver.executeUpdate("drop index name on person"); will drop the index for the "name" column. - *

driver.executeUpdate("ALTER TABLE person DROP primary key"); will drop the primary key. - *

driver.executeUpdate("update person set age=44, salary=3200.5 where name = 'guilherme campos hazan'"); - * will update the table. - *

driver.executeUpdate("delete person where name like 'g%'"); will delete records of the table. - *

driver.executeUpdate("insert into person (age, salary, name, email) - * values (32, 2000, 'guilherme campos hazan', 'guich@superwaba.com.br')"); will insert a record in the table. - * - * @param sql The SQL update command. - * @return The number of rows affected or 0 if a drop or alter operation was successful. - * @throws IllegalStateException If the driver is closed. - * @throws SQLParseException If an InvalidDateException or InvalidNumberException occurs. - * @throws DriverException If an IOException occurs. - */ - public int executeUpdate(String sql) throws IllegalStateException, SQLParseException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (logger != null) - synchronized (logger) - { - logger.log(Logger.INFO, sql, false); - } - - try - { - LitebaseParser parser = new LitebaseParser(); - - // juliana@202_5: removed possible NPE if there is a blank in the beginning of the sql command. - String tempSQL = sql.toLowerCase().trim(); - - parser.tableList = new SQLResultSetTable[1]; - if (tempSQL.startsWith("insert") || tempSQL.startsWith("update")) - { - parser.fieldValues = new String[SQLElement.MAX_NUM_COLUMNS]; - parser.fieldNames = new String[SQLElement.MAX_NUM_COLUMNS]; - } - else - if (tempSQL.startsWith("drop index")) - parser.fieldNames = new String[SQLElement.MAX_NUM_COLUMNS]; - else if (tempSQL.startsWith("alter table")) - if (tempSQL.indexOf("rename") != -1) - parser.fieldNames = new String[2]; - else if (tempSQL.indexOf("add primary key") != -1) - parser.fieldNames = new String[SQLElement.MAX_NUM_COLUMNS]; - else if (tempSQL.indexOf("add") != -1) // juliana@253_22: added command ALTER TABLE ADD column. - parser.fieldList = new SQLFieldDefinition[1]; - - // juliana@224_2: improved memory usage on BlackBerry. - LitebaseParser.parser(sql, parser, lexer); // Does the parsing. - - switch (parser.command) - { - case SQLElement.CMD_DROP_TABLE: // DROP TABLE - litebaseExecuteDropTable(parser); - return 0; - case SQLElement.CMD_DROP_INDEX: // DROP INDEX - return litebaseExecuteDropIndex(parser); - case SQLElement.CMD_INSERT: // INSERT - new SQLInsertStatement(parser, this).litebaseBindInsertStatement().litebaseDoInsert(this); - return 1; - case SQLElement.CMD_DELETE: // DELETE - return new SQLDeleteStatement(parser).litebaseBindDeleteStatement(this).litebaseDoDelete(this); - case SQLElement.CMD_UPDATE: // UPDATE - return new SQLUpdateStatement(parser).litebaseBindUpdateStatement(this).litebaseDoUpdate(this); - case SQLElement.CMD_ALTER_DROP_PK: // DROP PRIMARY KEY - case SQLElement.CMD_ALTER_ADD_PK: // ADD PRIMARY KEY - case SQLElement.CMD_ALTER_RENAME_TABLE: // RENAME TABLE - case SQLElement.CMD_ALTER_RENAME_COLUMN: // RENAME COLUMN - case SQLElement.CMD_ALTER_ADD_COLUMN: // ADD COLUMN // juliana@253_22: added command ALTER TABLE ADD column. - litebaseExecuteAlter(parser); - return 0; - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - return -1; - } - - // juliana@253_22: added command ALTER TABLE ADD column. - /** - * Executes an alter statement. - * - * @param parser The parser. - * @throws DriverException If there is no primary key to be dropped, an invalid column name, or a new table name is already in use. - * @throws AlreadyCreatedException If one tries to add another primary key or there is a duplicated column name in the primary key definition, or - * a simple primary key is added to a column that already has an index. - * @throws SQLParseException If there is a blob in a primary key definition. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private void litebaseExecuteAlter(LitebaseParser parser) throws DriverException, AlreadyCreatedException, SQLParseException, IOException, - InvalidDateException, InvalidNumberException - { - String tableName = parser.tableList[0].tableName; - Table table = getTable(tableName); - int colIndex = -1; - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last oppening. - table.setModified(); // Sets the table as not closed properly. - - switch (parser.command) - { - case SQLElement.CMD_ALTER_DROP_PK: // DROP PRIMARY KEY - if (table.primaryKeyCol != Utils.NO_PRIMARY_KEY) // Simple primary key. - { - colIndex = table.primaryKeyCol; - table.primaryKeyCol = Utils.NO_PRIMARY_KEY; - table.driverDropIndex(colIndex); // Drops the PK index. - } - else if (table.composedPK != Utils.NO_PRIMARY_KEY) // Composed primary key. - { - // juliana@230_17: solved a possible crash or exception if the table is not closed properly after dropping a composed primary key. - table.numberComposedPKCols = 0; - table.composedPK = Utils.NO_PRIMARY_KEY; - table.driverDropComposedIndex(table.composedPrimaryKeyCols, -1, true); // The meta data is saved. - table.composedPrimaryKeyCols = null; - } - else - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY)); // There's no primary key. - break; - - case SQLElement.CMD_ALTER_ADD_PK: // ADD PRIMARY KEY - // There can't be two primary keys. - if (table.primaryKeyCol != Utils.NO_PRIMARY_KEY || table.composedPK != Utils.NO_PRIMARY_KEY) - throw new AlreadyCreatedException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PRIMARY_KEY_ALREADY_DEFINED)); - - int size = parser.fieldNamesSize, - j = -1, - i = -1; - byte[] composedPKCols = new byte[size]; - String colName = null; - - while (++i < size) - { - colIndex = table.htName2index.get((colName = parser.fieldNames[i]).hashCode(), -1); - if (colIndex == -1) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + colName); - - // Verifies if there's a duplicate definition. - j = i; - while (--j >= 0) - if (composedPKCols[j] == colIndex) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATED_COLUMN_NAME) + parser.fieldNames[i]); - composedPKCols[i] = (byte)colIndex; - - if (table.columnTypes[colIndex] == SQLElement.BLOB) // A blob cant be in a primary key. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_PRIMARY_KEY)); - } - if (size == 1) // Simple primary key. - { - // juliana@230_41: an AlreadyCreatedException is now thrown when trying to add a primary key for a column that already has a simple - // index. - if (table.columnIndices[colIndex] != null) // If there is no index yet for the column, creates it. - throw new AlreadyCreatedException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INDEX_ALREADY_CREATED) + colIndex); - try - { - table.primaryKeyCol = colIndex; - driverCreateIndex(tableName, new String[] {colName}, null, true); - } - catch (PrimaryKeyViolationException exception) - { - table.primaryKeyCol = -1; - throw exception; - } - } - else // Composed primary key. - { - table.numberComposedPKCols = size; - - try - { - driverCreateIndex(tableName, null, table.composedPrimaryKeyCols = composedPKCols, true); - } - catch (PrimaryKeyViolationException exception) - { - table.numberComposedPKCols = 0; - table.composedPrimaryKeyCols = null; - table.composedPK = -1; - throw exception; - } - } - break; - - case SQLElement.CMD_ALTER_RENAME_TABLE: // RENAME TABLE - String newTableName = parser.fieldNames[0]; // The new table name is stored in the field list. - - if (exists(newTableName)) // The new table name can't be in use. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_ALREADY_EXIST) + newTableName); - try - { - table.renameTable(this, tableName, newTableName); - } - catch (IOException exception) // A possible fail during rename will rename back all table files. - { - table.renameTable(this, newTableName, tableName); - throw exception; - } - break; - - case SQLElement.CMD_ALTER_RENAME_COLUMN: // RENAME COLUMN - table.renameTableColumn(parser.fieldNames[1], parser.fieldNames[0]); - break; - - case SQLElement.CMD_ALTER_ADD_COLUMN: // ADD COLUMN - SQLFieldDefinition field = parser.fieldList[0]; - int oldCount = table.columnCount, - newCount = oldCount + 1, - bytes = (oldCount + 7) >> 3, - hash = field.fieldName.hashCode(); - - if (table.htName2index.exists(hash)) // The column name can't exist yet. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATED_COLUMN_NAME) + field.fieldName); - - if (oldCount == SQLElement.MAX_NUM_COLUMNS) // The maximum number of columns can't be exceeded. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMNS_OVERFLOW) + field.fieldName); - - if (((oldCount + 8) >> 3) > bytes) // Increases the nulls fields if the number of bytes must be increased. - { - byte[][] columnNulls = table.columnNulls; - columnNulls[0] = new byte[++bytes]; - columnNulls[1] = new byte[bytes]; - - if (columnNulls[2] != null) - columnNulls[2] = new byte[bytes]; - table.storeNulls = new byte[bytes]; - } - - // Increases all the columns. - table.ghas = new byte[newCount]; - table.gvOlds = new SQLValue[newCount]; - - // Column attrs. - byte[] newAttrs = new byte[newCount]; - Vm.arrayCopy(table.columnAttrs, 0, newAttrs, 0, oldCount); - (table.columnAttrs = newAttrs)[oldCount] = (byte)((field.defaultValue != null? Utils.ATTR_COLUMN_HAS_DEFAULT : 0) - | (field.isNotNull? Utils.ATTR_COLUMN_IS_NOT_NULL : 0)); - - // Column hashes. - int[] newHashes = new int[newCount]; - Vm.arrayCopy(table.columnHashes, 0, newHashes, 0, oldCount); - table.htName2index.put((table.columnHashes = newHashes)[oldCount] = hash, oldCount); - - // Column offsets. - short[] newOffsets = new short[newCount + 1]; - Vm.arrayCopy(table.columnOffsets, 0, newOffsets, 0, newCount); - (table.columnOffsets = newOffsets)[newCount] = (short)(newOffsets[oldCount] + Utils.typeSizes[field.fieldType]); - - // Column types. - byte[] newTypes = new byte[newCount]; - Vm.arrayCopy(table.columnTypes, 0, newTypes, 0, oldCount); - (table.columnTypes = newTypes)[oldCount] = (byte)field.fieldType; - - // Column sizes. - int[] newSizes = new int[newCount]; - Vm.arrayCopy(table.columnSizes, 0, newSizes, 0, oldCount); - (table.columnSizes = newSizes)[oldCount] = field.fieldSize; - - // Column names. - String[] newNames = new String[newCount]; - Vm.arrayCopy(table.columnNames, 0, newNames, 0, oldCount); - (table.columnNames = newNames)[oldCount] = field.fieldName; - - // Default values. - SQLValue[] newDefaultValues = new SQLValue[newCount]; - SQLValue newDefaultValue = null; - Vm.arrayCopy(table.defaultValues, 0, newDefaultValues, 0, oldCount); - table.defaultValues = newDefaultValues; - String defaultValue = field.defaultValue; - - if (defaultValue != null) // Sets the new default value if it exists. - { - newDefaultValue = newDefaultValues[oldCount] = new SQLValue(); - - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than - // the type range. - switch (field.fieldType) - { - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - if (defaultValue.length() > field.fieldSize) // The default value size can't be larger than the size of the field definition. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER)); - newDefaultValue.asString = defaultValue; - break; - case SQLElement.SHORT: - newDefaultValue.asShort = Convert.toShort(defaultValue); - break; - case SQLElement.INT: - newDefaultValue.asInt = Convert.toInt(defaultValue); - break; - case SQLElement.LONG: - newDefaultValue.asLong = Convert.toLong(defaultValue); - break; - case SQLElement.FLOAT: - newDefaultValue.asDouble = Utils.toFloat(defaultValue); - break; - case SQLElement.DOUBLE: - newDefaultValue.asDouble = Convert.toDouble(defaultValue); - case SQLElement.DATE: // juliana@224_2: improved memory usage on BlackBerry. - newDefaultValue.asInt = tempDate.set(defaultValue, Settings.DATE_YMD); - break; - case SQLElement.DATETIME: // juliana@224_2: improved memory usage on BlackBerry. - int pos = defaultValue.lastIndexOf(' '); - if (pos == -1) // There is no time here. - { - newDefaultValue.asInt = tempDate.set(defaultValue, Settings.DATE_YMD); - newDefaultValue.asShort = 0; - } - else - { - newDefaultValue.asInt = tempDate.set(defaultValue.substring(0, pos), Settings.DATE_YMD); - newDefaultValue.asShort = Utils.testAndPrepareTime(defaultValue.substring(pos + 1).trim()); - } - break; - } - } - - // Column indices. - Index[] newIndices = new Index[newCount]; - Vm.arrayCopy(table.columnIndices, 0, newIndices, 0, oldCount); - table.columnIndices = newIndices; - - // Sets the new plain db. - PlainDB newDB = new PlainDB(table.name + '_', sourcePath, true), - oldDB = table.db; - newDB.isAscii = oldDB.isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - newDB.headerSize = oldDB.headerSize; - newDB.driver = this; - boolean useCrypto = newDB.useCrypto = oldDB.useCrypto; // juliana@crypto_1: now Litebase supports weak cryptography. - int newRowSize = newOffsets[newCount] + ((newCount + 7) >> 3) + 4; - byte[] newBuffer = newDB.basbuf = new byte[newRowSize]; - newDB.setRowSize(newRowSize, newBuffer); - newDB.rowInc = oldDB.rowCount; - - // Sets the new streams. - ByteArrayStream newBas = newDB.bas; - DataStreamLB newBasds = newDB.basds; // juliana@crypto_1: now Litebase supports weak cryptography. - - // Sets some variables for reading and writing the records. - SQLValue[] record = SQLValue.newSQLValues(newCount); - byte[] columnNulls0 = table.columnNulls[0]; - int length = columnNulls0.length; - boolean isNull = (record[oldCount] = newDefaultValue) == null; - - // Saves the new meta data. - table.db = newDB; - table.columnCount++; - table.tableSaveMetaData(Utils.TSMD_EVERYTHING); - table.db = oldDB; - table.columnCount--; - - // juliana@230_12 - int crc32, - k; - int[] intArray = new int[1]; - - size = oldDB.rowCount; - i = -1; - while (++i < size) - { - table.readRecord(record, i, 0, null, null, false, null); // juliana@220_3 juliana@227_20 - j = -1; - - if (isNull) - columnNulls0[oldCount >> 3] |= (1 << (oldCount & 7)); // Sets the column as null. - - // juliana@230_44: solved a NullPointerException when purging a table with a null value on a CHAR or VARCHAR column. - // juliana@220_3 - while (++j < newCount) - newDB.writeValue(newTypes[j], record[j], newBasds, (columnNulls0[j >> 3] & (1 << (j & 7))) == 0, true, newSizes[j], 0, false); - - newBasds.writeBytes(columnNulls0, 0, length); - - // juliana@230_12: improved recover table to take .dbo data into consideration. - // juliana@223_8: corrected a bug on purge that would not copy the crc32 codes for the rows. - // juliana@220_4: added a crc32 code for every record. Please update your tables. - k = (useCrypto? newBuffer[3] ^ 0xAA : newBuffer[3]); - newBuffer[3] = (useCrypto? (byte)0xAA : 0); // juliana@222_5: The crc was not being calculated correctly for updates. - newDB.useOldCrypto = false; - - // Computes the crc for the record and stores at the end of the record. - crc32 = Table.updateCRC32(newBuffer, newBas.getPos(), 0, useCrypto); - if (table.version == Table.VERSION) - { - byte[] byteArray; - - j = newCount; - while (--j > 0) - if ((newTypes[j] == SQLElement.CHARS || newTypes[j] == SQLElement.CHARS_NOCASE) - && (columnNulls0[j >> 3] & (1 << (j & 7))) == 0) - { - byteArray = Utils.toByteArray(record[j].asString); - crc32 = Table.updateCRC32(byteArray, byteArray.length, crc32, false); - } - else if (newTypes[j] == SQLElement.BLOB && (columnNulls0[j >> 3] & (1 << (j & 7))) == 0) - { - intArray[0] = record[j].asBlob.length; - crc32 = Table.updateCRC32(Convert.ints2bytes(intArray, 4), 4, crc32, false); - } - } - newBasds.writeInt(crc32); - newBuffer[3] = (byte)k; - - newDB.add(); - newDB.write(); - } - - // Puts the new plain db in the table and deletes the old one. - newDB.rowInc = Utils.DEFAULT_ROW_INC; - ((NormalFile)oldDB.db).f.delete(); - ((NormalFile)oldDB.dbo).f.delete(); - newDB.rename(table.name, sourcePath); - table.db = newDB; - table.columnCount++; - - i = newCount; - while (--i >= 0) // Recreates the simple indices. - if (newIndices[i] != null && (newTypes[i] == SQLElement.CHARS || newTypes[i] == SQLElement.CHARS_NOCASE)) - table.tableReIndex(i, null, false); - - if ((i = table.numberComposedIndices) > 0) // Recreates the composed indices. - while (--i >= 0) - table.tableReIndex(i, table.composedIndices[i], false); - } - } - - /** - * Drops a table. - * - * @param parser The parser. - * @throws DriverException If the table does not exist. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private void litebaseExecuteDropTable(LitebaseParser parser) throws DriverException, IOException, InvalidDateException - { - String tableName = parser.tableList[0].tableName; - Table table = (Table)htTables.remove(tableName); // Tries to get the table. - - // flsobral@224_4: workaround for bug with listFiles on BlackBerry 9000. - - // juliana@253_5: removed .idr files from all indices and changed its format. - if (table != null) // The table is open. - { - Index idx; - int i = table.columnCount; - // Drops its simple indices. - while (--i >= 0) - if ((idx = table.columnIndices[i]) != null) - idx.fnodes.f.delete(); - // Drops its composed indices. - if ((i = table.numberComposedIndices) > 0) - while (--i >= 0) - table.composedIndices[i].index.fnodes.f.delete(); - table.db.remove(); // Drops the table. - } - else // The table is closed. - { - // juliana@220_12: drop table was dropping a closed table and all tables starting with the same name of the dropped one. - // Lists the folder files. - String path = sourcePath, - name = appCrid + '-' + tableName, - nameSimpIdx = name + '$', - nameCompIdx = name + '&'; - name += '.'; - File file = null; - String[] listFiles = new File(path).listFiles(); // Lists all the path files. - int numFiles = listFiles.length; - - while (--numFiles >=0) // Erases the table files. - if (listFiles[numFiles].startsWith(name) || listFiles[numFiles].startsWith(nameSimpIdx) || listFiles[numFiles].startsWith(nameCompIdx)) - (file = new File(path + listFiles[numFiles])).delete(); - - if (file == null) // If there is no file to be erased, an exception must be raised. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_NAME_NOT_FOUND) + tableName); - } - } - - /** - * Drops an index. - * - * @param parser The parser. - * @return The number of indices deleted. - * @throws DriverException If a column does not have an index, is invalid, or if the columns to have the index dropped are from a primary key. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private int litebaseExecuteDropIndex(LitebaseParser parser) throws DriverException, IOException, InvalidDateException - { - String tableName = parser.tableList[0].tableName; - String colName = parser.fieldNames[0]; - Table table = getTable(tableName); - int n = 1; - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last oppening. - table.setModified(); // Sets the table as not closed properly. - - if (colName.equals("*")) // Drops all the indices. - n = table.deleteAllIndices(); - else // Drops an especific index. - if (parser.fieldNamesSize == 1) // Simple index. - { - int column = table.htName2index.get(colName.hashCode(), -1); - - if (column == -1) // Unknown column. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + colName); - - if (column == table.primaryKeyCol) // Can't use drop index to drop a primary key. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DROP_PRIMARY_KEY)); - table.driverDropIndex(column); - } - else // Composed index. - - { - byte[] columns = new byte[parser.fieldNamesSize]; - byte[] keyCols = table.composedPrimaryKeyCols; - byte column; - int i = parser.fieldNamesSize; - - while (--i >= 0) - { - if ((column = (byte)table.htName2index.get((colName = parser.fieldNames[i]).hashCode(), -1)) == -1) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + colName); - columns[i] = column; - } - - if (keyCols != null) // Can't use drop index to drop a primary key. - { - // The columns passed can't be the same ones of the primary key. - i = columns.length; - while (--i >= 0) - if (columns[i] != keyCols[i]) - break; - if (i < 0) // The columns of both arrays are equal. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DROP_PRIMARY_KEY)); - } - table.driverDropComposedIndex(columns, -1, true); - } - return n; - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to execute queries in a table. Example: - * - *

-    * ResultSet rs = driver.executeQuery("select rowid, name, salary, age from person where age != 44");
-    * rs.afterLast();
-    * while (rs.prev())
-    *    Vm.debug(rs.getString(1) + ". " + rs.getString(2) + " - " + rs.getInt("age") + " years");
-    * 
- * - * @param sql The SQL query command. - * @return A result set with the values returned from the query. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - * @throws SQLParseException If an InvalidDateException or an InvalidNumberException occurs. - */ - public ResultSet executeQuery(String sql) throws IllegalStateException, DriverException, SQLParseException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger != null && !logOnlyChanges) - synchronized (logger) - { - logger.log(Logger.INFO, sql, false); - } - try - { - // Parses, creates and executes the select statement. - LitebaseParser parser = new LitebaseParser(); - parser.tableList = new SQLResultSetTable[SQLElement.MAX_NUM_COLUMNS]; - parser.select = new SQLSelectClause(); - - // juliana@253_9: improved Litebase parser. - - // juliana@224_2: improved memory usage on BlackBerry. - LitebaseParser.parser(sql, parser, lexer); // Does de parsing. - - return new SQLSelectStatement(parser).litebaseBindSelectStatement(this).litebaseDoSelect(this); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Creates a pre-compiled statement with the given sql. Prepared statements are faster for repeated queries. Instead of parsing the same query - * where only a few arguments change, it is better to create a prepared statement and the query is pre-parsed. Then, it is just needed to set the - * arguments (defined as ? in the sql) and run the sql. - * - * @param sql The SQL query command. - * @return A pre-compiled SQL statement. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - * @throws SQLParseException If an InvalidDateException or an InvalidNumberException occurs. - */ - public PreparedStatement prepareStatement(String sql) throws IllegalStateException, SQLParseException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger != null && !logOnlyChanges) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("prepareStatement ").append(sql)); - } - - // juliana@226_16: prepared statement is now a singleton. - PreparedStatement ps = (PreparedStatement)htPS.get(sql); - if (ps != null) - { - ps.clearParameters(); - return ps; - } - - ps = new PreparedStatement(); - try - { - ps.prepare(this, sql); - htPS.put(sql, ps); // guich@201_28 - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - return ps; - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the current rowid for a given table. - * - * @param tableName The name of a table. - * @return The current rowid for the table. -1 will never occur. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - */ - public int getCurrentRowId(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger != null && !logOnlyChanges) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("getCurrentRowId ").append(tableName)); - } - - try - { - return getTable(tableName).currentRowId; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return -1; - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the number of valid rows in a table. This may be different from the number of records if a row has been deleted. - * - * @see #getRowCountDeleted(String) - * @param tableName The name of a table. - * @return The number of valid rows in a table. -1 will never occur. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - */ - public int getRowCount(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger != null && !logOnlyChanges) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("getRowCount ").append(tableName)); - } - try // juliana@201_31: LitebaseConnection.getRowCount() will now throw an exception if tableName is null or invalid instead of returning -1. - { - Table table = getTable(tableName); - return table.db.rowCount - table.deletedRowsCount; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return -1; - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Sets the row increment used when creating or updating big amounts of data. Using this method greatly increases the speed of bulk insertions - * (about 3x faster). To use it, it is necessary to call it (preferable) with the amount of rows that will be inserted. After the insertion is - * finished, it is NECESSARY to call it again, passing -1 as the increment argument. Without doing this last step, data may - * be lost because some writes will be delayed until the method is called with -1. Another good optimization on bulk insertions is to drop the - * indexes and then create them afterwards. So, to correctly use setRowInc(), it is necessary to: - * - *
-    * driver.setRowInc("table", totalNumberOfRows);
-    * // Fetches the data and insert them.
-    * driver.setRowInc("table", -1);
-    * 
- * - * Using prepared statements on insertion makes it another a couple of times faster. - * - * @param tableName The associated table name. - * @param inc The increment value. - * @throws IllegalStateException If the driver is closed. - * @throws IllegalArgumentException If the increment is equal to 0 or less than -1. - * @throws DriverException If an IOException occurs. - */ - public void setRowInc(String tableName, int inc) throws DriverException, IllegalStateException, IllegalArgumentException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - if (inc == 0 || inc < -1) - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_INC)); - - if (logger != null) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("setRowInc ").append(tableName).append(' ').append(inc)); - } - - try - { - Table table = getTable(tableName); - PlainDB db = table.db; - Index[] columnIndices = table.columnIndices; - ComposedIndex[] composedIndices = table.composedIndices; - boolean setting = inc != -1; - db.rowInc = setting? inc : Utils.DEFAULT_ROW_INC; - int i = table.columnCount; - while (--i >= 0) // Flushes the simple indices. - if (columnIndices[i] != null) - columnIndices[i].setWriteDelayed(setting); - i = table.numberComposedIndices; - while (--i >= 0) // juliana@202_18: The composed indices must also be written delayed when setting row increment to a value different to -1. - composedIndices[i].index.setWriteDelayed(setting); - - NormalFile dbFile = (NormalFile)db.db, - dboFile = (NormalFile)db.dbo; - - // juliana@227_3: improved table files flush dealing. - if (inc == -1) // juliana@202_17: Flushs the files to disk when setting row increment to -1. - { - dbFile.dontFlush = dboFile.dontFlush = false; - if (dbFile.cacheIsDirty) - dbFile.flushCache(); // Flushs .db. - if (dboFile.cacheIsDirty) - dboFile.flushCache(); // Flushs .dbo. - } - else - dbFile.dontFlush = dboFile.dontFlush = true; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Indicates if the given table already exists. This method can be used before a drop table. - * - * @param tableName The name of a table. - * @return true if a table exists; false othewise. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs or the driver is closed. - */ - public boolean exists(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - String name = tableName.toLowerCase(); - - if (htTables.exists(name)) // If the table is loaded, it exists. - return true; - - try // Tests if the .db file exists. - { - sBuffer.setLength(0); - boolean ret = new File(sBuffer.append(sourcePath).append(appCrid).append('-').append(name).append(NormalFile.DB_EXT).toString()).exists(); - - // juliana@253_10: now a DriverException will be thown if the .db file exists but not .dbo. - if (ret && !new File(name = sBuffer.append('o').toString()).exists()) - throw new FileNotFoundException(name); - - return ret; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Releases the file handles (on the device) of a Litebase instance. Note that, after this is called, all Resultsets and - * PreparedStatements created with this Litebase instance will be in an inconsistent state, and using them will probably reset the - * device. This method also deletes the active instance for this creator id from Litebase's internal table. - * - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs or the driver is closed. - */ - public void closeAll() throws IllegalStateException, DriverException // guich@109 - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (logger != null) - synchronized (logger) - { - logger.log(Logger.INFO, "closeAll", false); - } - try - { - litebaseClose(); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Releases the file handles (on the device) of a Litebase instance. Note that, after this is called, all Resultsets and - * PreparedStatements created with this Litebase instance will be in an inconsistent state, and using them will probably reset the - * device. - * - * @throws IOException If an internal method throws it. - */ - private void litebaseClose() throws IOException - { - dontFinalize = true; - Vector v = htTables.getValues(); - int n = v.size(); - - Index idx; - Table table; - int i; - - while (--n >= 0) - { - // juliana@253_8: now Litebase supports weak cryptography. - (table = (Table)v.items[n]).db.close(true); // Closes the table files. - table.db = null; - - // Closes the simple indices. - i = table.columnCount; - while (--i >= 0) - if ((idx = table.columnIndices[i]) != null) - idx.close(); - - // Closes the composed indices. - i = table.numberComposedIndices; - while (--i >= 0) - table.composedIndices[i].index.close(); - } - - htTables = null; // guich@564_9: makes sure that this instance will fail if someone tries to use it again. - synchronized (htDrivers) // juliana@201_11: only sets the LitebaseConnection class as finalized if all its objects are finalized. - { - htDrivers.remove(key); - } - - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@201_13: .dbo is now being purged. - /** - * Used to delete physically the records of the given table. Records are always deleted logically, to avoid the need of recreating the indexes. - * When a new record is added, it doesn't uses the position of the previously deleted one. This can make the table big, if a table is created, - * filled and has a couple of records deleted. This method will remove all deleted records and recreate the indexes accordingly. Note that it - * can take some time to run. - *

- * Important: the rowid of the records is NOT changed with this operation. - * - * @param tableName The table name to purge. - * @return The number of purged records. -1 will never occur. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - */ - public int purge(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (logger != null) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("purge ").append(tableName)); - } - - try - { - Table table = getTable(tableName); - - // Removes the deleted records from the table. - int deleted = table.deletedRowsCount; - - if (deleted > 0 || table.wasUpdated) // juliana@270_27: now purge will also really purge the table if it only suffers updates. - { - PlainDB plainDB = table.db; - NormalFile dbFile = (NormalFile)plainDB.db; - - int i = -1, - j, - rows = plainDB.rowCount, - willRemain = rows - table.deletedRowsCount, - columns = table.columnCount; - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified - // since its last oppening. - table.setModified(); // Sets the table as not closed properly. - - if (willRemain == 0) // If no rows will remain, just deletes everyone. - { - // Shrinking the file is faster than deleting and recreating it. - dbFile.f.setSize(0); - ((NormalFile)plainDB.dbo).f.setSize(0); - ((NormalFile)plainDB.dbo).finalPos = 0; // juliana@202_13: .dbo final position must be zeroed when purging all the table. - - dbFile.size = plainDB.dbo.size = plainDB.rowAvail = plainDB.rowCount = 0; - } - else - { - // rnovais@570_75: inserts all records at once. - PlainDB newdb = new PlainDB(table.name + '_', sourcePath, true); - DataStreamLB oldBasds = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - int[] columnSizes = table.columnSizes; - byte[] columnTypes = table.columnTypes; - byte[] columnNulls0 = table.columnNulls[0]; - - boolean useCrypto = newdb.useCrypto = plainDB.useCrypto; // juliana@253_8: now Litebase supports weak cryptography. - SQLValue[] record = SQLValue.newSQLValues(table.columnCount); - int length = columnNulls0.length; - - // juliana@220_13: The header size of the purged table must be equal to the header size of the table before purge. - newdb.headerSize = plainDB.headerSize; - - newdb.isAscii = plainDB.isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - - newdb.driver = this; - - // rnovais@570_61: verifies if it needs to store the currentRowId. - plainDB.read(rows - 1); - if ((oldBasds.readInt() & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED) // Is the last record deleted? - table.auxRowId = table.currentRowId; - - newdb.setRowSize(plainDB.rowSize, plainDB.basbuf); - newdb.rowInc = willRemain; - - ByteArrayStream newBas = newdb.bas; - DataStreamLB newBasds = newdb.basds; // juliana@253_8: now Litebase supports weak cryptography. - byte[] oldBuffer = plainDB.bas.getBuffer(); - - // juliana@230_12 - int crc32, - k; - int[] intArray = new int[1]; - - while (++i < rows) - { - table.readRecord(record, i, 0, null, null, false, null); // juliana@220_3 juliana@227_20 - if (((record[0].asInt = oldBasds.readInt()) & Utils.ROW_ATTR_MASK) != Utils.ROW_ATTR_DELETED) // Is record not deleted? - { - j = -1; - - // juliana@230_44: solved a NullPointerException when purging a table with a null value on a CHAR or VARCHAR column. - while (++j < columns) - newdb.writeValue(columnTypes[j], record[j], newBasds, ((columnNulls0[j >> 3] & (1 << (j & 7))) == 0), true, columnSizes[j], 0, false); // juliana@220_3 - newBasds.writeBytes(columnNulls0, 0, length); - - // juliana@230_12: improved recover table to take .dbo data into consideration. - // juliana@223_8: corrected a bug on purge that would not copy the crc32 codes for the rows. - // juliana@220_4: added a crc32 code for every record. Please update your tables. - k = oldBuffer[3]; - oldBuffer[3] = (useCrypto? (byte)0xAA : 0); // juliana@222_5: The crc was not being calculated correctly for updates. - - // Computes the crc for the record and stores at the end of the record. - crc32 = Table.updateCRC32(oldBuffer, newBas.getPos(), 0, useCrypto); - - if (table.version == Table.VERSION) - { - byte[] byteArray; - - j = columns; - while (--j > 0) - if ((columnTypes[j] == SQLElement.CHARS || columnTypes[j] == SQLElement.CHARS_NOCASE) - && (columnNulls0[j >> 3] & (1 << (j & 7))) == 0) - { - byteArray = Utils.toByteArray(record[j].asString); - crc32 = Table.updateCRC32(byteArray, byteArray.length, crc32, false); - } - else if (columnTypes[j] == SQLElement.BLOB && (columnNulls0[j >> 3] & (1 << (j & 7))) == 0) - { - intArray[0] = record[j].asBlob.length; - crc32 = Table.updateCRC32(Convert.ints2bytes(intArray, 4), 4, crc32, false); - } - } - - newBasds.writeInt(crc32); - - oldBuffer[3] = (byte)k; - - newdb.add(); - newdb.write(); - } - } - newdb.rowInc = Utils.DEFAULT_ROW_INC; - dbFile.f.delete(); - ((NormalFile)plainDB.dbo).f.delete(); - newdb.rename(table.name, sourcePath); - newdb.useOldCrypto = false; - table.db = newdb; - } - - // juliana@115_8: saving metadata before recreating the indices does not let .db header become empty. - - // Empties the deletedRows and update the metadata. - table.deletedRowsCount = 0; - table.wasUpdated = false; // juliana@270_27: now purge will also really purge the table if it only suffers updates. - table.tableSaveMetaData(Utils.TSMD_EVERYTHING); // guich@560_24 - - Vm.gc(); // Frees some memory. - - i = table.columnCount; - while (--i >= 0) // Recreates the simple indices. - if (table.columnIndices[i] != null) - table.tableReIndex(i, null, false); - - if ((i = table.numberComposedIndices) > 0) // Recreates the composed indices. - while (--i >= 0) - table.tableReIndex(i, table.composedIndices[i], false); - } - return deleted; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return -1; - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the number of deleted rows. - * - * @param tableName The name of a table. - * @return The total number of deleted records of the given table. -1 will never occur. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs. - */ - public int getRowCountDeleted(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger != null && !logOnlyChanges) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("getRowCountDeleted ").append(tableName)); - } - - try - { - return getTable(tableName).deletedRowsCount; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return -1; - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Gets an iterator for a table. With it, it is possible iterate through all the rows of a table in sequence and get - * its attributes. This is good for synchronizing a table. While the iterator is active, it is not possible to do any - * queries or updates because this can cause dada corruption. - * - * @param tableName The name of a table. - * @return A iterator for the given table. null will never occur. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs or the driver is closed. - */ - public RowIterator getRowIterator(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger != null && !logOnlyChanges) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("getRowIterator ").append(tableName)); - } - try - { - return new RowIterator(this, tableName.toLowerCase()); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return null; - } - } - - // juliana@210_3: LitebaseConnection.getLogger() and LitebaseConnection.setLogger() are no longer deprecated. - /** - * Gets the Litebase logger. The fields should be used unless using the logger within threads. - * - * @return The logger. - */ - public static synchronized Logger getLogger() - { - return logger; - } - - /** - * Sets the litebase logger. This enables log messages for all queries and statements of Litebase and can be very useful to help finding bugs in - * the system. Logs take up memory space, so turn them on only when necessary. The fields should be used unless using the logger within threads. - * - * @param logger The logger. - */ - public static synchronized void setLogger(Logger logger) - { - LitebaseConnection.logger = logger; - } - - // juliana@230_4: Litebase default logger is now a plain text file instead of a PDB file. - /** - * Gets the default Litebase logger. When this method is called for the first time, a new text file is created. In the subsequent calls, the same - * file is used. - * - * @return The default Litebase logger. - * @throws DriverException If an IOException occurs. - */ - public static synchronized Logger getDefaultLogger() throws DriverException - { - Logger logger = Logger.getLogger("litebase", -1, null); // Gets the logger object. - - try - { - if (logger.getOutputHandlers().length == 0) // Only gets a new default logger if no one exists. - { - LitebaseConnection.tempTime.update(); - logger.addOutputHandler(new File(Convert.appendPath(Settings.dataPath != null && Settings.dataPath.length() > 0? Settings.dataPath - : Settings.appPath, "LITEBASE_" + LitebaseConnection.tempTime.getTimeLong() + '.' + Settings.applicationId + ".LOGS"), File.CREATE_EMPTY)); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - - logger.setLevel(Logger.INFO); - return logger; - } - - /** - * Deletes all the log files with the default format found in the default device folder. If log is enabled, the current log file is not affected - * by this command. - * - * @return the number of files deleted. - * @throws DriverException If an IOException occurs. - */ - public static synchronized int deleteLogFiles() throws DriverException - { - String path = Settings.dataPath != null && Settings.dataPath.length() > 0? Settings.dataPath : Settings.appPath; - int count = 0, // The number of log files. - i; - - try // Gets a list of closed log files. - { - String[] list = File.listFiles(path); - - if (list != null) - { - String current; - - i = list.length; - while (--i >= 0) - { - current = list[i]; - if (current.startsWith(Convert.appendPath(path, "LITEBASE_")) && current.endsWith(".LOGS")) - { - try - { - new File(current, File.READ_WRITE).delete(); // Deletes all closed log files. - count++; - } - catch (IOException exception) {} - } - } - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - return count; - } - - // guich@566_32 rnovais@570_77 - /** - * This is a handy method that can be used to reproduce all commands of a log file. This is intended to be used by the development team only. - * Here's a sample on how to use it: - * - *

-    * String []sql =
-    * {
-    *    "new LitebaseConnection(MBSL,null)",
-    *    "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20), 
-    *                                DESCRICAO char(100), UNDCAIXA char(10), PESO float, UNIDADEMEDIDA char(3),
-    *                                EMBALAGEM char(10), PORCTROCA float, PERMITETROCA int)",
-    *    "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)",
-    *    "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)",
-    *    "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)",
-    *    "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)",
-    *    "closeAll",
-    *    "new LitebaseConnection(MBSL,null)",
-    *    "insert into PRODUTO values(1,'19132', 2, 1, '1', 2, '3', 'ABSORVENTE SILHO ABAS', '5', 13, 'PCT', '20X30', 10, 0)",
-    *  };
-    *  LitebaseConnection.processLogs(sql, true);
-    * 
- * - * @param sql The string array of SQL commands to be executed. - * @param params The parameters to open a connection. - * @param isDebug Indicates if debug information is to displayed on the debug console. - * @return The LitebaseConnection instance created, or null if closeAll was the last command executed (or no commands - * were executed at all). - * @throws DriverException If an exception occurs. - */ - public static LitebaseConnection processLogs(String[] sql, String params, boolean isDebug) throws DriverException - { - LitebaseConnection driver = null; - int i = -1, - length = sql.length; - String str; - ResultSet rs; - - while (++i < length) - { - str = sql[i]; - if (isDebug) - Vm.debug("running command #" + (i + 1)); - try - { - if (str.startsWith("new LitebaseConnection")) // Gets a new Litebase Connection. - - // juliana@115_3: corrected the start and the end position for the application id of the table. - driver = getInstance(str.substring(23, 27), params); - else - - if (str.startsWith("create")) // Create command. - driver.execute(str); - else - if (str.equals("closeAll")) // closeAll() command. - { - driver.closeAll(); - driver = null; - } - else - if (str.startsWith("select")) // Select command. - { - rs = driver.executeQuery(str); - while (rs.next()); - rs.close(); - } - else - if (str.length() > 0) // Commands that update the table. - driver.executeUpdate(str); - } - catch (RuntimeException exception) - { - if (isDebug) - Vm.debug(str + " - " + exception); - throw new DriverException(exception); - } - } - return driver; - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@220_5: added a method to recover possible corrupted tables, the ones that were not closed properly. - /** - * Tries to recover a table not closed properly by marking and erasing logically the records whose crc are not valid. The table must be closed in - * order to use this method and will be closed after it. - * When a table is not closed, a TableNotClosedException is thrown when one tries to access the table. One should, then, call this - * method to try to recover it. - * - * @param tableName The table to be recovered. - * @return true if it was in fact corrupted; falseotherwise. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If an IOException occurs, it is not possible to read from the file, or the table was closed correctly. - */ - public boolean recoverTable(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (logger != null) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("recover table ").append(tableName)); - } - - try - { - sBuffer.setLength(0); - - // Opens the table file. - File tableDb = new File(sBuffer.append(sourcePath).append(appCrid).append('-').append(tableName.toLowerCase()).append(".db").toString(), - File.READ_WRITE); - - byte[] buffer = oneByte; - boolean useCryptoAux = useCrypto; - - // juliana@222_2: the table must be not closed properly in order to recover it. - tableDb.setPos(6); - if (tableDb.readBytes(buffer, 0, 1) == -1) - { - tableDb.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_READ)); - } - - if (useCryptoAux) // juliana@253_8: now Litebase supports weak cryptography. - buffer[0] ^= 0xAA; - - if ((buffer[0] & Table.IS_SAVED_CORRECTLY) == Table.IS_SAVED_CORRECTLY) - { - tableDb.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_CLOSED)); - } - tableDb.close(); - - boolean recovered = false; - Table table = new Table(); - - // juliana@224_2: improved memory usage on BlackBerry. - - // Opens the table even if it was not cloded properly. - // juliana@253_8: now Litebase supports weak cryptography. - table.tableCreate(sourcePath, appCrid + '-' + tableName.toLowerCase(), false, appCrid, this, isAscii, useCrypto, false); - - PlainDB plainDB = table.db; - ByteArrayStream bas = plainDB.bas; - DataStreamLB dataStream = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - buffer = bas.getBuffer(); - boolean corrupted; - int rows = plainDB.rowCount, - crc32, - rowid, - i = rows, - - // juliana@230_12: improved recover table to take .dbo data into consideration. - j, - columnCount = table.columnCount, - - len = buffer.length - 4; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - SQLValue[] record = SQLValue.newSQLValues(columnCount); - byte[] columnNulls0 = table.columnNulls[0]; - byte[] byteArray; - byte[] types = table.columnTypes; - int[] intArray = new int[1]; - boolean useOldCrypto = plainDB.useOldCrypto; - - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - int auxRowId = -1, - currentRowId = -1, - deletedRowsCount = 0; // Invalidates the number of deleted rows. - - while (--i >= 0) // Checks all table records. - { - plainDB.read(i); - corrupted = false; - - if (isZero(buffer)) // juliana@268_3: Now does not do anything if there are only zeros in a row and removes them. - { - rows--; - continue; - } - - rowid = dataStream.readInt(); - - if ((rowid & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED) // Counts the number of deleted records. - deletedRowsCount++; - else - { - bas.reset(); - buffer[3] = (useCryptoAux? (byte)0xAA : 0); // Erases rowid information. - - // juliana@230_12: improved recover table to take .dbo data into consideration. - crc32 = Table.updateCRC32(buffer, len, 0, useCryptoAux); - - if (table.version == Table.VERSION) - { - j = columnCount; - while (--j > 0) - record[j].asInt = -1; - - try - { - table.readRecord(record, i, 0, null, null, false, null); - - j = columnCount; - while (--j > 0) - if ((types[j] == SQLElement.CHARS || types[j] == SQLElement.CHARS_NOCASE) - && (columnNulls0[j >> 3] & (1 << (j & 7))) == 0) - { - byteArray = Utils.toByteArray(record[j].asString); - crc32 = Table.updateCRC32(byteArray, byteArray.length, crc32, false); - } - else if (types[j] == SQLElement.BLOB && (columnNulls0[j >> 3] & (1 << (j & 7))) == 0) - { - intArray[0] = record[j].asInt; - crc32 = Table.updateCRC32(Convert.ints2bytes(intArray, 4), 4, crc32, false); - } - } - catch (DriverException exception) - { - corrupted = true; - } - } - - dataStream.skipBytes(len); - if (useOldCrypto) - { - dataStream.writeInt(crc32); - plainDB.rewrite(i); - table.auxRowId = (rowid & Utils.ROW_ID_MASK) + 1; - } - else if (crc32 != dataStream.readInt() && !corrupted) // Deletes and invalidates corrupted records. - { - bas.reset(); - dataStream.writeInt(Utils.ROW_ATTR_DELETED); - plainDB.rewrite(i); - deletedRowsCount++; - recovered = true; - - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - if (currentRowId < 0) - currentRowId = (rowid & Utils.ROW_ID_MASK) + 1; - } - else // juliana@224_3: corrected a bug that would make Litebase not use the correct rowid after a recoverTable(). - { - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - rowid = (rowid & Utils.ROW_ID_MASK) + 1; - if (currentRowId < 0) - currentRowId = rowid; - if (auxRowId < 0) - auxRowId = rowid; - } - - } - } - - plainDB.rowCount = rows; - table.deletedRowsCount = deletedRowsCount; - - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - table.currentRowId = currentRowId; - table.auxRowId = auxRowId; - - // Recreates the indices. - // Simple indices. - i = table.columnIndices.length; - Index[] indices = table.columnIndices; - while (--i >= 0) - if (indices[i] != null) - table.tableReIndex(i, null, false); - - // Composed indices. - i = table.numberComposedIndices; - ComposedIndex[] compIndices = table.composedIndices; - while (--i >= 0) - table.tableReIndex(i, compIndices[i], false); - - // juliana@224_3: corrected a bug that would make Litebase not use the correct rowid after a recoverTable(). - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - table.tableSaveMetaData(Utils.TSMD_EVERYTHING); // Saves information concerning deleted rows and the auxiliary rowid. - - // Closes the table. - // juliana@253_8: now Litebase supports weak cryptography. - plainDB.rowCount = rows; - plainDB.useOldCrypto = false; - plainDB.close(true); // Closes the table files. - table.db = null; - Index idx; - - // Closes the simple indices. - i = table.columnCount; - while (--i >= 0) - if ((idx = table.columnIndices[i]) != null) - idx.close(); - - // Closes the composed indices. - i = table.numberComposedIndices; - while (--i >= 0) - table.composedIndices[i].index.close(); - - return recovered; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return true; - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@220_11: added a method to convert a table from the previous format to the current one being used. - /** - * Converts a table from the previous Litebase table version to the current one. If the table format is older than the previous table version, - * this method can't be used. It is possible to know if the table version is not compativel with the current version used in Litebase because - * an exception will be thrown if one tries to open a table with the old format. The table will be closed after using this method and must be - * closed before calling it. Notice that the table .db file will be overwritten. - * - * @param tableName The name of the table to be converted. - * @throws IllegalStateException If the driver is closed. - * @throws DriverException If the table version is not the previous one (too old or the actual used by Litebase), it is not possible to read from - * the file, or an IllegalArgumentIOException, FileNotFoundException, or IOException occurs. - */ - public void convert(String tableName) throws IllegalStateException, DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (logger != null) - synchronized (logger) - { - sBuffer.setLength(0); - logger.logInfo(sBuffer.append("convert ").append(tableName)); - } - - try - { - byte[] bytes = new byte[2]; - Table table = new Table(); - int rowid, - version; - - sBuffer.setLength(0); - - // Opens the table file. - File tableDb = new File(sBuffer.append(sourcePath).append(appCrid).append('-').append(tableName.toLowerCase()).append(".db").toString(), - File.READ_WRITE); - - // The version must be the previous of the current one. - tableDb.setPos(7); - if (tableDb.readBytes(bytes, 0, 2) == -1) - { - tableDb.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_READ)); - } - - if (useCrypto) // juliana@253_8: now Litebase supports weak cryptography. - { - bytes[0] = bytes[0] ^= 0xAA; - bytes[1] = bytes[1] ^= 0xAA; - } - - if ((version = (((bytes[1] & 0xFF) << 8) | (bytes[0] & 0xFF))) != Table.VERSION - 1 || version != Table.VERSION - 2) - { - tableDb.close(); // juliana@222_4: The table files must be closed if convert() fails(). - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_WRONG_PREV_VERSION) + tableName); - } - - // Changes the version to be current one and closes it. - tableDb.setPos(7); - oneByte[0] = (byte)(useCrypto? Table.VERSION ^ 0xAA : Table.VERSION); // juliana@253_8 - tableDb.writeBytes(oneByte, 0, 1); - tableDb.close(); - - // juliana@224_2: improved memory usage on BlackBerry. - - // Opens the table even if it was not cloded properly. - // juliana@253_8: now Litebase supports weak cryptography. - table.tableCreate(sourcePath, appCrid + '-' + tableName.toLowerCase(), false, appCrid, this, isAscii, useCrypto, false); - PlainDB plainDB = table.db; - NormalFile dbFile = (NormalFile)table.db.db; - DataStreamLB dataStream = plainDB.basds; - ByteArrayStream bas = plainDB.bas; - byte[] buffer = bas.getBuffer(); - int headerSize = plainDB.headerSize, - len = buffer.length - 4, - rows = (dbFile.size - headerSize) / len, - - // juliana@230_12: improved recover table to take .dbo data into consideration. - columnCount = table.columnCount, - i, - crc32; - byte[] columnNulls0 = table.columnNulls[0]; - int[] intArray = new int[1]; - byte[] types = table.columnTypes; - SQLValue[] record = SQLValue.newSQLValues(columnCount); - byte[] byteArray; - boolean useCrypto = plainDB.useCrypto; - - while (--rows >= 0) // Converts all the records adding a crc code to them. - { - dbFile.setPos(rows * len + headerSize); - dbFile.readBytes(buffer, 0, len); - rowid = (useCrypto? buffer[3] ^ 0xAA: buffer[3]); - buffer[3] = (useCrypto? (byte)0xAA : 0); - bas.reset(); - dataStream.skipBytes(len); - - // juliana@230_12: improved recover table to take .dbo data into consideration. - crc32 = Table.updateCRC32(buffer, len, 0, useCrypto); - - i = columnCount; - while (--i > 0) - record[i].asInt = -1; - - table.readRecord(record, rows, 0, null, null, false, null); - - i = columnCount; - while (--i > 0) - if ((types[i] == SQLElement.CHARS || types[i] == SQLElement.CHARS_NOCASE) - && (columnNulls0[i >> 3] & (1 << (i & 7))) == 0) - { - byteArray = Utils.toByteArray(record[i].asString); - crc32 = Table.updateCRC32(byteArray, byteArray.length, crc32, false); - } - else if (types[i] == SQLElement.BLOB && (columnNulls0[i >> 3] & (1 << (i & 7))) == 0) - { - intArray[0] = record[i].asInt; - crc32 = Table.updateCRC32(Convert.ints2bytes(intArray, 4), 4, crc32, false); - } - - dataStream.writeInt(crc32); - buffer[3] = (byte)rowid; - plainDB.rewrite(rows); - } - - // Closes the table. - // juliana@253_8: now Litebase supports weak cryptography. - plainDB.close(false); // Closes the table files. - table.db = null; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - } - - /** - * Finalizes the LitebaseConnection object. - * - * @throws IOException If an internal method throws it. - */ - protected void finalize() throws IOException - { - litebaseClose(); - } - - /** - * Creates an index. - * - * @param tableName The table name whose index is to be created. - * @param columnNames The names of the index columns. - * @param numberColumns The column numbers of the index. - * @param isPK Indicates if the index to be created is the primary key. - * @throws IOException If an internal method throws it. - * @throws DriverException If a column for the index does not exist. - * @throws SQLParseException If a column for the index is of type blob. - * @throws PrimaryKeyViolationException If an index already exist for a group or one column. - * @throws InvalidDateException If an internal method throws it. - */ - private void driverCreateIndex(String tableName, String[] columnNames, byte[] numberColumns, boolean isPK) - throws IOException, DriverException, PrimaryKeyViolationException, InvalidDateException, SQLParseException - { - Table table = getTable(tableName); - PlainDB plainDB = table.db; - int indexCount = (numberColumns == null)? columnNames.length : numberColumns.length; - int saveType, - idx = -1, - i = indexCount; - byte[] columns = new byte[indexCount]; - int[] columnSizes = new int[indexCount]; - byte[] columnTypes = new byte[indexCount]; - - while (--i >= 0) - { - // Column not found. - if ((idx = columns[i] = (numberColumns == null)? (byte)table.htName2index.get(columnNames[i].hashCode(), -1) : numberColumns[i]) == -1) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND) + columnNames[i]); - - if (table.columnTypes[idx] == SQLElement.BLOB) // An index can't have a blob column. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_INDEX)); - - columnSizes[i] = table.columnSizes[idx]; - columnTypes[i] = table.columnTypes[idx]; - } - - int newIndexNumber = table.verifyIfIndexAlreadyExists(columns); - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last oppening. - table.setModified(); // Sets the table as not closed properly. - - // juliana@253_5: removed .idr files from all indices and changed its format. - if (indexCount == 1) - { - table.indexCreateIndex(table.name, columns[0], columnSizes, columnTypes, appCrid, sourcePath, false); - saveType = Utils.TSMD_EVERYTHING; - } - else - { - table.indexCreateComposedIndex(table.name, columns, columnSizes, columnTypes, newIndexNumber, isPK, appCrid, true, sourcePath, false); - saveType = Utils.TSMD_EVERYTHING; - } - - if (plainDB.rowCount > 0) // luciana@570_49: fixed index creation when row count == 1. - { - try // Catchs the PrimaryKeyViolation exception to drop the recreated index and throws it again. - { - if (indexCount == 1) - table.tableReIndex(idx, null, isPK); - else - { - int id = (newIndexNumber < 0)? -newIndexNumber : newIndexNumber; - table.tableReIndex(id - 1, table.composedIndices[id - 1], isPK); - } - } - catch (PrimaryKeyViolationException exception) - { - if (indexCount == 1) - { - table.primaryKeyCol = Utils.NO_PRIMARY_KEY; - table.driverDropIndex(idx); - } - else - { - table.composedPK = Utils.NO_PRIMARY_KEY; - table.numberComposedPKCols = 0; - table.driverDropComposedIndex(columns, table.numberComposedIndices - 1, true); - } - throw exception; - } - catch (DriverException exception) - { - if (indexCount == 1) - table.driverDropIndex(idx); - else - table.driverDropComposedIndex(columns, table.numberComposedIndices - 1, true); - if (isPK) - { - table.primaryKeyCol = table.composedPK = Utils.NO_PRIMARY_KEY; - table.numberComposedPKCols = 0; - table.composedPrimaryKeyCols = null; - } - throw exception; - } - } - table.tableSaveMetaData(saveType); // guich@560_24 - } - - // Modified it to return the table that was created, to add columnNames, and primaryKeyCol to the parameters list. - /** - * Creates a table, which can be stored on disk or on memory (result set table). - * - * @param tableName The table name. - * @param names The table column names. - * @param hashes The table column hashes. - * @param types The table column types. - * @param sizes The table column sizes. - * @param columnAttrs The table cxlumn attributes. - * @param defaultValues The table column default values. - * @param primaryKeyCol The primary key column. - * @param composedPK The composed primary key index in the composed indices. - * @param composedPKCols The columnns that are part of the composed primary key. - * @return The table handle. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - Table driverCreateTable(String tableName, String[] names, int[] hashes, byte[] types, int[] sizes, byte[] columnAttrs, SQLValue[] defaultValues, - int primaryKeyCol, int composedPK, byte[] composedPKCols) throws IOException, InvalidDateException - { - Table table = new Table(); - - // juliana@224_2: improved memory usage on BlackBerry. - // rnovais@570_75 juliana@220_5 - // juliana@253_8: now Litebase supports weak cryptography. - table.tableCreate(sourcePath, tableName == null? null : appCrid + "-" + tableName, true, appCrid, this, isAscii, useCrypto, true); - - if (tableName == null) // juliana@201_14 - { - table.db.headerSize = 0; - table.tableSetMetaData(null, hashes, types, sizes, null, null, -1, -1, null, 0); - } - else - { - int numberComposedPKCols = composedPKCols != null? composedPKCols.length : 0; - - table.isModified = true; - - // Stores the meta data if the table is new. - table.tableSetMetaData(names, hashes, types, sizes, columnAttrs, defaultValues, primaryKeyCol, composedPK, composedPKCols, - numberComposedPKCols); - htTables.put(tableName, table); - - // juliana@224_2: improved memory usage on BlackBerry. - - if (primaryKeyCol != Utils.NO_PRIMARY_KEY) // creates the index for the primary key. - driverCreateIndex(tableName, new String[] {names[primaryKeyCol]}, null, false); - if (numberComposedPKCols > 0) // Creates the composed index for the composed primary key. - driverCreateIndex(tableName, null, composedPKCols, true); - } - - return table; - } - - /** - * Fetches a table that already exists. - * - * @param tableName The name of the table to be fetched. - * @return The desired table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - Table getTable(String tableName) throws IOException, InvalidDateException - { - Table table = (Table)htTables.get(tableName = tableName.toLowerCase()); // Already open? - - if (table == null) - { - // Opens it. - table = new Table(); - - // juliana@224_2: improved memory usage on BlackBerry. - // juliana@253_8: now Litebase supports weak cryptography. - table.tableCreate(sourcePath, appCrid + '-' + tableName, false, appCrid, this, isAscii, useCrypto, true); // juliana@220_5 - - PlainDB plainDB = table.db; - - if (plainDB.db.size == 0) // Only valid if already created. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_NAME_NOT_FOUND) + tableName); - - htTables.put(tableName, table); // Puts the table in the table hashes. - } - - return table; - } - - /** - * Tests if a path is valid. It can't be null or relative. - * - * @param path The path to be tested. - * @throws DriverException If the path is null or relative. - */ - private static void validatePath(String path) throws DriverException - { - int pathLen; - String auxP; - - if (path == null || (pathLen = path.length()) == 0) // The path can't be null or empty. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PATH) + path); - - // The path can't be relative. - if (path.charAt(0) == '.') - { - if (pathLen == 1 || (pathLen == 2 && path.charAt(1) == '.')) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PATH) + path + '.'); - if (pathLen > 1 && (path.charAt(1) == '/' || path.charAt(1) == '\\')) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PATH) + path + '.'); - if (pathLen > 2 && path.charAt(1) == '.' && (path.charAt(2) == '/' || path.charAt(2) == '\\')) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PATH) + path + '.'); - } - - pathLen = path.indexOf("/."); - if (pathLen >= 0 ) - auxP = path.substring(pathLen); - else - auxP = null; - if (auxP != null && (auxP.length() == 2 || auxP.charAt(2) == '/' || (auxP.charAt(2) == '.' && (auxP.length() == 3 || auxP.charAt(3) == '/')))) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PATH) + path + '.'); - } - - /** - * Used to returned the slot where the tables were stored on Palm OS. Not used anymore. - * - * @return -1. - * @deprecated Not used anymore. - */ - public int getSlot() // juliana@223_1: added a method to get the current slot being used. Returns -1 except on palm. - { - return -1; - } - - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@226_6: added LitebaseConnection.isOpen(), which indicates if a table is open in the current connection. - /** - * Indicates if a table is open or not. - * - * @param tableName The table name to be checked - * @return true if the table is open in the current connection; false, otherwise. - * @throws IllegalStateException If the driver is closed. - */ - public boolean isOpen(String tableName) throws IllegalStateException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - return htTables.get(tableName = tableName.toLowerCase()) != null; - } - - // juliana@226_10: added LitebaseConnection.dropDatabase(). - /** - * Drops all the tables from a database represented by its application id and path. - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param slot Not used anymore. - * @throws DriverException If the database is not found or an IOException occurs. - */ - public static void dropDatabase(String crid, String sourcePath, int slot) throws DriverException - { - try - { - // Lists all the files of the folder. - String[] files = (new File(sourcePath, File.DONT_OPEN)).listFiles(); - int i = files.length; - - crid += '-'; - - // Deletes only the files of the chosen database. - boolean deleted = false; - while (--i >= 0) - { - if (files[i].startsWith(crid)) - { - new File(sourcePath + files[i], File.DONT_OPEN).delete(); - deleted = true; - } - } - - if (!deleted) // If the database has no files, there is an error. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DB_NOT_FOUND)); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@250_5: added LitebaseConnection.isTableProperlyClosed() and LitebaseConnection.listAllTables(). - /** - * Indicates if a table is closed properly or not. - * - * @param tableName The table to be verified. - * @return true if the table is closed properly or is open (a not properly closed table can't be opened); false, - * otherwise. - * @throws DriverException If the file can't be read or an IOException occurs. - * @throws IllegalStateException If the driver is closed. - */ - public boolean isTableProperlyClosed(String tableName) throws DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - if (!isOpen(tableName)) - { - try - { - // Opens the table file. - sBuffer.setLength(0); - File tableDb = new File(sBuffer.append(sourcePath).append(appCrid).append('-').append(tableName.toLowerCase()).append(".db").toString(), - File.READ_WRITE); - byte[] buffer = oneByte; - - // Reads the flag. - tableDb.setPos(6); - if (tableDb.readBytes(buffer, 0, 1) == -1) - { - tableDb.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_READ)); - } - - if ((buffer[0] & Table.IS_SAVED_CORRECTLY) == Table.IS_SAVED_CORRECTLY) - { - tableDb.close(); - return true; // The table was closed properly. - } - tableDb.close(); - return false; // The table was not closed properly. - } - - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - return true; // The table is open, so it was closed properly. - } - - /** - * Lists all table names of the current connection. - * - * @return An array of all the table names of the current connection. If the current connection has no tables, an empty list is returned. - * @throws DriverException If an IOException occurs. - * @throws IllegalStateException If the driver is closed. - */ - public String[] listAllTables() throws DriverException - { - if (htTables == null) // The driver can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - - try - { - String[] files = (new File(sourcePath, File.DONT_OPEN)).listFiles(); - String fileName, - crid = appCrid + '-'; - int i = files.length, - count = 0; - - while (--i >= 0) // Selects the .db files that are from the tables of the current connection. - { - if ((fileName = files[i]).startsWith(crid) && fileName.endsWith(".db")) - { - files[i] = fileName.substring(5, fileName.length() - 3); - count++; - } - else - files[i] = null; - } - - // Gets only the table names that are from this connection. - String[] names = new String[count]; - i = files.length; - while (--i >= 0) - if (files[i] != null) - names[--count] = files[i]; - - return names; - - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Encrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param slot Not used anyore. - */ - public static void encryptTables(String crid, String sourcePath, int slot) - { - encDecTables(crid, sourcePath, true); - } - - // juliana@253_16: created static methods LitebaseConnection.encryptTables() and decryptTables(). - /** - * Decrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param slot Not used anymore. - */ - public static void decryptTables(String crid, String sourcePath, int slot) - { - encDecTables(crid, sourcePath, false); - } - - /** - * Encrypts or decrypts all the tables of a connection given from the application id. - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param toEncrypt Indicates if the tables are to be encrypted or decrypted. - * @throws DriverException If an IOException is thrown or not all the tables use the desired cryptography format. - */ - private static void encDecTables(String crid, String sourcePath, boolean toEncrypt) throws DriverException - { - try - { - // Lists all the files of the folder. - String[] files = (new File(sourcePath, File.DONT_OPEN)).listFiles(); - int i = files.length, - j, - k; - File file; - byte[] bytes = new byte[NormalFile.CACHE_INITIAL_SIZE]; - - crid += '-'; - - // Before doing anything, checks if all the .db files are marked as decrypted or encrypted. - while (--i >= 0) - { - if (files[i].startsWith(crid) && files[i].endsWith(".db")) - { - (file = new File(sourcePath + files[i], File.READ_ONLY)).readBytes(bytes, 0, 4); - file.close(); - if ((toEncrypt? bytes[0] != 0 : (bytes[0] != 1 && bytes[0] != 3)) || bytes[1] != 0 || bytes[2] != 0 || bytes[3] != 0) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_WRONG_CRYPTO_FORMAT)); - } - } - - i = files.length; - int encByte = toEncrypt? 3 : 0; - while (--i >= 0) - { - if (files[i].startsWith(crid)) - { - file = new File(sourcePath + files[i], File.READ_WRITE); - - if (files[i].endsWith(".db")) // Changes the .db file crypto information. - { - file.readBytes(bytes, 0, 4); - bytes[0] = (byte)encByte; - file.setPos(0); - file.writeBytes(bytes, 0, 4); - } - - // Encrypts or decrypts all the table files data. - while ((k = j = file.readBytes(bytes, 0, NormalFile.CACHE_INITIAL_SIZE)) != -1) - { - while (--k >= 0) - bytes[k] ^= 0xAA; - file.setPos(-j, File.SEEK_CUR); - file.writeBytes(bytes, 0, j); - } - - file.close(); - } - - } - - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Indicates if a buffer is only composed by zeros or not. - * - * @param buffer The buffer. - * @return true if the buffer is only composed by zeros; false, otherwise. - */ - private boolean isZero(byte[] buffer) - { - int i = buffer.length; - - while (--i >= 0) - if (buffer[i] != 0) - return false; - return true; - } -} diff --git a/LitebaseSDK/src/java/litebase/LitebaseConnection4D.java b/LitebaseSDK/src/java/litebase/LitebaseConnection4D.java deleted file mode 100644 index 3196273375..0000000000 --- a/LitebaseSDK/src/java/litebase/LitebaseConnection4D.java +++ /dev/null @@ -1,697 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.*; -import totalcross.util.Logger; - -/** - * This class is the native one used to issue SQL commands. Read Litebase Companion chapters for more information. - */ -@Deprecated -public class LitebaseConnection4D -{ - /** - * English language. - */ - public static final int LANGUAGE_EN = 1; - - /** - * Portuguese language. - */ - public static final int LANGUAGE_PT = 2; - - // guich@223_10: the Litebase version is not declared as final anymore, otherwise the compiler replaces the constant by the value. - /** - * The string corresponding to the current Litebase version. - */ - public static String versionStr = "2.8.5"; - - /** - * The integer corresponding to the current Litebase version. - */ - public static int version = 285; - - /** - * Current build number. - */ - public static int buildNumber = 000; - - /** - * Indicates if the tables of this connection use ascii or unicode strings. - */ - boolean isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - - /** - * Indicates if the tables of this connection use cryptography. - */ - private boolean useCrypto; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * A flag that indicates that this class has already been finalized. - */ - boolean dontFinalize; - - /** - * The key which identifies one Litebase connection instance. - */ - int key; - - /** - * The creator id for the tables managed by Litebase. - */ - int appCrid; - - /** - * Given the table name, returns the Table structure. - */ - long htTables; - - /** - * The source path, where the tables will be stored. - */ - long sourcePath; - - /** - * A hash table of prepared statements. - */ - long htPS; // juliana@226_16 - - /** - * An array of node indices. - */ - long nodes; // juliana@253_6: the maximum number of keys of a index was duplicated. - - /** - * Indicates if the native library is already attached. - */ - private static boolean isDriverLoaded; - - /** - * The logger. - */ - public static Logger logger; - - // juliana@230_30: reduced log files size. - /** - * A StringBuffer to hold the logger string. - */ - static StringBuffer loggerString = new StringBuffer(); - - // juliana@211_1: language is now a public field. It must be accessed directly. - /** - * The language of the Litebase messages. - */ - public static int language = LANGUAGE_EN; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - /** - * Indicates if only changes during Litebase operation must be logged or not. - */ - public static boolean logOnlyChanges; - - // juliana@222_10: corrected a bug that would possibly not load Litebase native library on Android inside a thread. - static - { - // Attachs the native library. - if (!isDriverLoaded && !(isDriverLoaded = totalcross.sys.Vm.attachNativeLibrary("Litebase"))) - if (language == LitebaseConnection.LANGUAGE_EN) - throw new DriverException("Can't find native methods implementation for LitebaseConnection. Please install Litebase.dll/prc file."); - else - throw new DriverException("N�o � poss�vel encontrar a implementa��o dos m�todos nativos para o LitebaseConnection. Por favor, instale o arquivo Litebase.dll/prc."); - } - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - private LitebaseConnection4D() {} - - // juliana@201_26: created a default getInstance() which creates a new Litebase connection with the current application id. - /** - * Creates a Litebase connection for the default creator id, storing the database as a flat file. - * This method avoids the creation of more than one instance with the same creator id, which would - * lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @return A Litebase instance. - */ - public static LitebaseConnection4D getInstance() { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateGetInstance(); - } - - /** - * Creates a Litebase connection for the default creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @return A Litebase instance. - */ - private static native LitebaseConnection4D privateGetInstance(); - - /** - * Creates a Litebase connection for the given creator id, storing the database as a flat file. - * This method avoids the creation of more than one instance with the same creator id, which would - * lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param appCrid The creator id, which may (or not) be the same one of the current application - * and MUST be 4 characters long. - * @return A Litebase instance. - */ - public static LitebaseConnection4D getInstance(String appCrid) { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateGetInstance(appCrid); - } - - /** - * Creates a Litebase connection for the given creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param appCrid The creator id, which may (or not) be the same one of the current application and MUST be 4 characters long. - * @return A Litebase instance. - * @throws DriverException If an application id with more or less than four characters is specified. - * @throws NullPointerException If appCrid == null. - */ - private static native LitebaseConnection4D privateGetInstance(String appCrid) throws DriverException, NullPointerException; - - /** - * Creates a LitebaseConnection for the given creator id and with the given connection param list. - * This method avoids the creation of more than one instance with the same creator id and - * parameters, which would lead to performance and memory problems. - * - * @param appCrid The creator id, which may be the same one of the current application and MUST be - * 4 characters long. - * @param params Only the folder where it is desired to store the tables, null, if it - * is desired to use the current data path, or - * chars_type = chars_format; path = source_path[;crypto] , where chars_format - * can be ascii or unicode, source_path is the - * folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is - * passed as a parameter, unicode is used and there is no cryptography. Notice that path must - * be absolute, not relative. - *

Note that databases belonging to multiple applications can be stored in the same path, - * since all tables are prefixed by the application's creator id. - *

Also notice that to store Litebase files on card on Pocket PC, just set the second - * parameter to the correct directory path. - *

It is not recommended to create the databases directly on the PDA. Memory cards are FIVE - * TIMES SLOWER than the main memory, so it will take a long time to create the tables. Even - * if the NVFS volume is used, it can be very slow. It is better to create the tables on the - * desktop, and copy everything to the memory card or to the NVFS volume. - *

Due to the slowness of a memory card and the NVFS volume, all queries will be stored in - * the main memory; only tables and indexes will be stored on the card or on the NVFS volume. - *

An exception will be raised if tables created with an ascii kind of connection are - * oppened with an unicode connection and vice-versa. - * @return A Litebase instance. - */ - public static LitebaseConnection4D getInstance(String appCrid, String params) { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateGetInstance(appCrid, params); - } - - /** - * Creates a LitebaseConnection for the given creator id and with the given connection param list. This method avoids the creation of more than - * one instance with the same creator id and parameters, which would lead to performance and memory problems. - * - * @param appCrid The creator id, which may be the same one of the current application and MUST be 4 characters long. - * @param params Only the folder where it is desired to store the tables, null, if it is desired to use the current data - * path, or chars_type = chars_format; path = source_path[;crypto] , where chars_format can be ascii or - * unicode, source_path is the folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is passed as a parameter, unicode is used and there is no - * cryptography. Notice that path must be absolute, not relative. - *

Note that databases belonging to multiple applications can be stored in the same path, since all tables are prefixed by the application's - * creator id. - *

Also notice that to store Litebase files on card on Pocket PC, just set the second parameter to the correct directory path. - *

It is not recommended to create the databases directly on the PDA. Memory cards are FIVE TIMES SLOWER than the main memory, so it will take - * a long time to create the tables. Even if the NVFS volume is used, it can be very slow. It is better to create the tables on the desktop, and - * copy everything to the memory card or to the NVFS volume. - *

Due to the slowness of a memory card and the NVFS volume, all queries will be stored in the main memory; only tables and indexes will be - * stored on the card or on the NVFS volume. - *

An exception will be raised if tables created with an ascii kind of connection are oppened with an unicode connection and vice-versa. - * @return A Litebase instance. - * @throws DriverException If an application id with more or less than four characters is specified. - * @throws NullPointerException If appCrid == null. - */ - private static native LitebaseConnection4D privateGetInstance(String appCrid, String params) throws DriverException, NullPointerException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the path where the tables created/opened by this connection are stored. - * - * @return A string representing the path. - * @throws IllegalStateException If the driver is closed. - */ - public native String getSourcePath() throws IllegalStateException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to execute a create table or create index SQL commands. - * - *

Examples: - *

    - *
  • driver.execute("create table PERSON (NAME CHAR(30), SALARY DOUBLE, AGE INT, EMAIL CHAR(50))"); - *
  • driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - *
- * - *

When creating an index, its name is ignored but must be given. The index can be created after data was added to the table. - * - * @param sql The SQL creation command. - */ - public native void execute(String sql); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to execute updates in a table (insert, delete, update, alter table, drop). E.g.: - * - *

driver.executeUpdate("drop table person"); will drop also the indices. - *

driver.executeUpdate("drop index * on person"); will drop all indices but not the primary key index. - *

driver.executeUpdate("drop index name on person"); will drop the index for the "name" column. - *

driver.executeUpdate("ALTER TABLE person DROP primary key"); will drop the primary key. - *

driver.executeUpdate("update person set age=44, salary=3200.5 where name = 'guilherme campos hazan'"); - * will update the table. - *

driver.executeUpdate("delete person where name like 'g%'"); will delete records of the table. - *

driver.executeUpdate("insert into person (age, salary, name, email) - * values (32, 2000, 'guilherme campos hazan', 'guich@superwaba.com.br')"); will insert a record in the table. - * - * @param sql The SQL update command. - * @return The number of rows affected or 0 if a drop or alter operation was successful. - */ - public native int executeUpdate(String sql); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to execute queries in a table. Example: - * - *

-    * ResultSet rs = driver.executeQuery("select rowid, name, salary, age from person where age != 44");
-    * rs.afterLast();
-    * while (rs.prev())
-    *    Vm.debug(rs.getString(1) + ". " + rs.getString(2) + " - " + rs.getInt("age") + " years");
-    * 
- * - * @param sql The SQL query command. - * @return A result set with the values returned from the query. - */ - public native ResultSet executeQuery(String sql); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Creates a pre-compiled statement with the given sql. Prepared statements are faster for repeated queries. Instead of parsing the same query - * where only a few arguments change, it is better to create a prepared statement and the query is pre-parsed. Then, it is just needed to set the - * arguments (defined as ? in the sql) and run the sql. - * - * @param sql The SQL query command. - * @return A pre-compiled SQL statement. - * @throws OutOfMemoryError If there is not enough memory to create the preparedStatement. - */ - public native PreparedStatement4D prepareStatement(String sql) throws OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the current rowid for a given table. - * - * @param tableName The name of a table. - * @return The current rowid for the table. - */ - public native int getCurrentRowId(String tableName); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@201_31: LitebaseConnection.getRowCount() will now throw an exception if tableName is null or invalid instead of returning -1. - /** - * Returns the number of valid rows in a table. This may be different from the number of records if a row has been deleted. - * - * @see #getRowCountDeleted(String) - * @param tableName The name of a table. - * @return The number of valid rows in a table. - */ - public native int getRowCount(String tableName); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Sets the row increment used when creating or updating big amounts of data. Using this method greatly increases the - * speed of bulk insertions (about 3x faster). To use it, it is necessary to call it (preferable) with the amount of - * lines that will be inserted. After the insertion is finished, it is NECESSARY to call it again, passing - * -1 as the increment argument. Without doing this last step, data may be lost because some writes will - * be delayed until the method is called with -1. Another good optimization on bulk insertions is to drop the indexes - * and then create them afterwards. So, to correctly use setRowInc(), it is necessary to: - * - *
-    * driver.setRowInc("table", totalNumberOfRows);
-    * // Fetch the data and insert them.
-    * driver.setRowInc("table", -1);
-    * 
- * - * Using prepared statements on insertion makes it another a couple of times faster. - * - * @param tableName The associated table name. - * @param inc The increment value. - * @throws IllegalArgumentException If the increment is equal to 0 or less than -1. - */ - public native void setRowInc(String tableName, int inc) throws IllegalArgumentException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Indicates if the given table already exists. This method can be used before a drop table. - * - * @param tableName The name of a table. - * @return true if a table exists; false othewise. - * @throws DriverException If tableName or path is too big. - */ - public native boolean exists(String tableName) throws DriverException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Releases the file handles (on the device) of a Litebase instance. Note that, after this is called, all Resultsets and - * PreparedStatements created with this Litebase instance will be in an inconsistent state, and using them will probably reset the - * device. This method also deletes the active instance for this creator id from Litebase's internal table. - * - * @throws IllegalStateException If the driver is closed. - */ - public native void closeAll() throws IllegalStateException; // guich@109 - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Used to delete physically the records of the given table. Records are always deleted logically, to avoid the need of recreating the indexes. - * When a new record is added, it doesn't uses the position of the previously deleted one. This can make the table big, if a table is created, - * filled and has a couple of records deleted. This method will remove all deleted records and recreate the indexes accordingly. Note that it - * can take some time to run. - *

- * Important: the rowid of the records is NOT changed with this operation. - * - * @param tableName The table name to purge. - * @return The number of purged records. - * @throws DriverException If a row can't be read or written. - * @throws OutOfMemoryError If there is not enough memory to purge the table. - */ - public native int purge(String tableName) throws DriverException, OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the number of deleted rows. - * - * @param tableName The name of a table. - * @return The total number of deleted records of the given table. - */ - public native int getRowCountDeleted(String tableName); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Gets an iterator for a table. With it, it is possible iterate through all the rows of a table in sequence and get - * its attributes. This is good for synchronizing a table. While the iterator is active, it is not possible to do any - * queries or updates because this can cause dada corruption. - * - * @param tableName The name of a table. - * @return A iterator for the given table. - */ - public native RowIterator4D getRowIterator(String tableName); - - // juliana@210_3: LitebaseConnection.getLogger() and LitebaseConnection.setLogger() are no longer deprecated. - /** - * Gets the Litebase logger. The fields should be used unless using the logger within threads. - * - * @return The logger. - */ - public static Logger getLogger() { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateGetLogger(); - } - - /** - * Gets the Litebase logger. The fields should be used unless using the logger within threads. - * - * @return The logger. - * @throws DriverException if an IOException occurs. - */ - private static native Logger privateGetLogger() throws DriverException; - - /** - * Sets the litebase logger. This enables log messages for all queries and statements of Litebase - * and can be very useful to help finding bugs in the system. Logs take up memory space, so turn - * them on only when necessary. The fields should be used unless using the logger within threads. - * - * @param logger The logger. - */ - public static void setLogger(Logger logger) { - while (!isDriverLoaded) { - Thread.yield(); - } - privateSetLogger(logger); - } - - /** - * Sets the litebase logger. This enables log messages for all queries and statements of Litebase and can be very useful to help finding bugs in - * the system. Logs take up memory space, so turn them on only when necessary. The fields should be used unless using the logger within threads. - * - * @param logger The logger. - */ - private static native void privateSetLogger(Logger logger); - - /** - * Gets the default Litebase logger. When this method is called for the first time, a new - * PDBFile is created and a log record started. In the subsequent calls, the same - * PDBFile is used, but in different log records. - * - * @return The default Litebase logger. - */ - public static Logger getDefaultLogger() { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateGetDefaultLogger(); - } - - /** - * Gets the default Litebase logger. When this method is called for the first time, a new PDBFile is created and a log record - * started. In the subsequent calls, the same PDBFile is used, but in different log records. - * - * @return The default Litebase logger. - * @throws DriverException If an IOException occurs. - */ - private static native Logger privateGetDefaultLogger() throws DriverException; - - /** - * Deletes all log files found in the device. If log is enabled, the current log file is not - * affected by this command. It only deletes PDB log files. - * - * @return the number of files deleted. - */ - public static int deleteLogFiles() { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateDeleteLogFiles(); - } - - /** - * Deletes all log files found in the device. If log is enabled, the current log file is not affected by this command. It only deletes PDB log - * files. - * - * @return the number of files deleted. - */ - private static native int privateDeleteLogFiles(); - - // guich@566_32 rnovais@570_77 - /** - * This is a handy method that can be used to reproduce all commands of a log file. This is - * intended to be used by the development team only. Here's a sample on how to use it: - * - *

-   * String []sql =
-   * {
-   *    "new LitebaseConnection(MBSL,null)",
-   *    "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20),
-   *                                DESCRICAO char(100), UNDCAIXA char(10), PESO float, UNIDADEMEDIDA char(3),
-   *                                EMBALAGEM char(10), PORCTROCA float, PERMITETROCA int)",
-   *    "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)",
-   *    "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)",
-   *    "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)",
-   *    "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)",
-   *    "closeAll",
-   *    "new LitebaseConnection(MBSL,null)",
-   *    "insert into PRODUTO values(1,'19132', 2, 1, '1', 2, '3', 'ABSORVENTE SILHO ABAS', '5', 13, 'PCT', '20X30', 10, 0)",
-   *  };
-   *  LitebaseConnection.processLogs(sql, true);
-   * 
- * - * @param sql The string array of SQL commands to be executed. - * @param params The parameters to open a connection. - * @param isDebug Indicates if debug information is to displayed on the debug console. - * @return The LitebaseConnection instance created, or null if closeAll - * was the last command executed (or no commands were executed at all). - */ - public static LitebaseConnection4D processLogs(String[] sql, String params, boolean isDebug) { - while (!isDriverLoaded) { - Thread.yield(); - } - return privateProcessLogs(sql, params, isDebug); - } - - /** - * This is a handy method that can be used to reproduce all commands of a log file. This is intended to be used by the development team only. - * Here's a sample on how to use it: - * - *
-    * String []sql =
-    * {
-    *    "new LitebaseConnection(MBSL,null)",
-    *    "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20), 
-    *                                DESCRICAO char(100), UNDCAIXA char(10), PESO float, UNIDADEMEDIDA char(3),
-    *                                EMBALAGEM char(10), PORCTROCA float, PERMITETROCA int)",
-    *    "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)",
-    *    "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)",
-    *    "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)",
-    *    "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)",
-    *    "closeAll",
-    *    "new LitebaseConnection(MBSL,null)",
-    *    "insert into PRODUTO values(1,'19132', 2, 1, '1', 2, '3', 'ABSORVENTE SILHO ABAS', '5', 13, 'PCT', '20X30', 10, 0)",
-    *  };
-    *  LitebaseConnection.processLogs(sql, true);
-    * 
- * - * @param sql The string array of SQL commands to be executed. - * @param params The parameters to open a connection. - * @param isDebug Indicates if debug information is to displayed on the debug console. - * @return The LitebaseConnection instance created, or null if closeAll was the last command executed (or no commands - * were executed at all). - * @throws DriverException If an exception occurs. - * @throws NullPointerException If sql is null. - * @throws OutOfMemoryError If a memory allocation fails. - */ - private static native LitebaseConnection4D privateProcessLogs(String[] sql, String params, boolean isDebug) throws DriverException, - NullPointerException, OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@220_5: added a method to recover possible corrupted tables, the ones that were not closed properly. - /** - * Tries to recover a table not closed properly by marking and erasing logically the records whose crc are not valid. - * - * @param tableName The table to be recovered. - * @return The number of purged records. - * @throws DriverException If the table name or path is too big. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native boolean recoverTable(String tableName) throws DriverException, OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@220_11: added a method to convert a table from the previous format to the current one being used. - /** - * Converts a table from the previous Litebase table version to the current one. If the table format is older than the previous table version, - * this method can't be used. It is possible to know if the table version is not compativel with the current version used in Litebase because - * an exception will be thrown if one tries to open a table with the old format. The table will be closed after using this method. Notice that - * the table .db file will be overwritten. - * - * @param tableName The name of the table to be converted. - * @throws DriverException If the table version is not the previous one (too old or the actual used by Litebase) or table name or path is too big. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native void convert(String tableName) throws DriverException, OutOfMemoryError; - - /** - * Finalizes the LitebaseConnection object. - */ - protected void finalize() - { - closeAll(); - } - - /** - * Used to returned the slot where the tables were stored on Palm OS. Not used anymore. - * - * @return -1. - * @deprecated Not used anymore. - */ - public native int getSlot(); // juliana@223_1: added a method to get the current slot being used. Returns -1 except on palm. - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@226_6: added LitebaseConnection.isOpen(), which indicates if a table is open in the current connection. - /** - * Indicates if a table is open or not. - * - * @param tableName The table name to be checked - * @return true if the table is open in the current connection; false, otherwise. - * @throws DriverException If the table name is too big. - */ - public native boolean isOpen(String tableName) throws DriverException; - - // juliana@226_10: added LitebaseConnection.dropDatabase(). - /** - * Drops all the tables from a database represented by its application id and path. - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param slot Not used anymore. - * @throws DriverException If the database is not found or a file error occurs. - * @throws NullPointerException If one of the string parameters is null. - */ - public native static void dropDatabase(String crid, String sourcePath, int slot) throws DriverException, NullPointerException; - - // juliana@250_5: added LitebaseConnection.isTableProperlyClosed() and LitebaseConnection.listAllTables(). - /** - * Indicates if a table is closed properly or not. - * - * @param tableName The table to be verified. - * @return true if the table is closed properly or is open (a not properly closed table can't be opened); false, - * otherwise. - * @throws DriverException If the table is corrupted. - * @throws NullPointerException If tableName is null. - */ - public native boolean isTableProperlyClosed(String tableName) throws DriverException, NullPointerException; - - /** - * Lists all table names of the current connection. If the current connection has no tables, an empty list is returned. - * - * @return An array of all the table names of the current connection. - * @throws DriverException If a file error occurs. - * @throws IllegalStateException If the driver is closed. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native String[] listAllTables() throws DriverException, IllegalStateException, OutOfMemoryError; - - // juliana@253_16: created static methods LitebaseConnection.encryptTables() and decryptTables(). - /** - * Encrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param slot Not used anymore. - */ - public native static void encryptTables(String crid, String sourcePath, int slot); - - /** - * Decrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param crid The application id of the database. - * @param sourcePath The path where the files are stored. - * @param slot Not used anymore. - */ - public native static void decryptTables(String crid, String sourcePath, int slot); -} diff --git a/LitebaseSDK/src/java/litebase/LitebaseLex.java b/LitebaseSDK/src/java/litebase/LitebaseLex.java deleted file mode 100644 index d1f1f7c351..0000000000 --- a/LitebaseSDK/src/java/litebase/LitebaseLex.java +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.*; - -/** - * Used to do SQL lexical analysis. - */ -@Deprecated -class LitebaseLex -{ - // Constants used to differenciate the kinds of tokens. - /** - * Indicates that a character is from 'A' to 'Z' or 'a' to 'z'. - */ - private static final int IS_ALPHA = 1; - - /** - * Indicates that a character is from '0' to '9'. - */ - private static final int IS_DIGIT = 2; - - /** - * Indicates that a character is + or -. - */ - private static final int IS_SIGN = 4; - - /** - * Indicates that a character is a possible end of a number. Its values can be: - * - *
  • 'd' or 'D' - double
  • - *
  • 'f' or 'F' - float
  • - *
  • 'l' or 'L' - long
  • - *
    - */ - private static final int IS_END_NUM = 8; - - /** Indicates that a character is the part of a relational operator. It can be: '!', '>', or '<'. - */ - private static final int IS_RELATIONAL = 16; - - /** - * Indicates that a character is a punctuation symbol. It can be: '.', '?', ',', or '='. - */ - private static final int IS_PUNCT = 32; - - /** - * Indicates that a character is an operator symbol. It can be: '+', '-', '*', '\', '^', '(', or ')'. - */ - private static final int IS_OPERATOR = 64; - - /** - * Indicates that a character is from 'a' to 'z', from 'A' to 'Z', from '0' to '9', or is an underscore '_'. - */ - private static final int IS_ALPHA_DIGIT = IS_ALPHA | IS_DIGIT; - - /** - * Indicates that a character is the beginning of a number. It can be a digit or a sign. - */ - private static final int IS_START_DIGIT = IS_DIGIT | IS_SIGN; - - /** - * This character denotes the end of file. - */ - static final int YYEOF = -1; // juliana@253_9: improved Litebase parser. - - /** - * The parser. - */ - LitebaseParser yyparser; // juliana@224_2: improved memory usage on BlackBerry. - - /** - * The input device. - */ - String zzReaderChars; // juliana@224_2: improved memory usage on BlackBerry. - - /** - * Hash table with the reserved words. - */ - private static ReservedHashtable reserved; // juliana@213_7: changed to Hashtable. - - /** - * The last char read. - */ - int yycurrent; // juliana@224_2: improved memory usage on BlackBerry. - - /** - * The last position of the buffer read. - */ - int yyposition; - - /** - * The name of the token - */ - StringBuffer nameToken; - - /** - * An array to help the selection of the kind of token. - */ - private static byte[] is = new byte[256]; - - static - { - // Initiates the array to select the kind of token. - // Gives the values for the letters. - Convert.fill(is, 'a', 'z' + 1, IS_ALPHA); - Convert.fill(is, 'A', 'Z' + 1, IS_ALPHA); - - // Letters denoting types of numbers can also be the end of the number. - is['d'] |= IS_END_NUM; - is['D'] |= IS_END_NUM; - is['f'] |= IS_END_NUM; - is['F'] |= IS_END_NUM; - is['l'] |= IS_END_NUM; - is['L'] |= IS_END_NUM; - - Convert.fill(is, '0', '9' + 1, IS_DIGIT); // Gives the values for the digits. - - is['_'] = IS_ALPHA_DIGIT; // '_' can be part of an identifier. - is['+'] = is['-'] = IS_SIGN; // + and - are only sign of numbers. - is['*'] = is['('] = is[')'] = IS_OPERATOR; // The other operators and brackets. - is['<'] = is['>'] = is['!'] = IS_RELATIONAL; // The symbols that can represent double tokens. - is['.'] = is[','] = is['?'] = is['='] = IS_PUNCT; // The symbols that are treated as punctuations and '='. - - // juliana@213_7: changed to Hashtable. - // Creates and populates the hash table of reserved words. - reserved = new ReservedHashtable(61); - - // juliana@224_2: improved memory usage on BlackBerry. - reserved.put("abs", LitebaseParser.TK_ABS); - reserved.put("add", LitebaseParser.TK_ADD); - reserved.put("alter", LitebaseParser.TK_ALTER); - reserved.put("and", LitebaseParser.TK_AND); - reserved.put("as", LitebaseParser.TK_AS); - reserved.put("asc", LitebaseParser.TK_ASC); - reserved.put("avg", LitebaseParser.TK_AVG); - reserved.put("blob", LitebaseParser.TK_BLOB); - reserved.put("by", LitebaseParser.TK_BY); - reserved.put("char", LitebaseParser.TK_CHAR); - reserved.put("count", LitebaseParser.TK_COUNT); - reserved.put("create", LitebaseParser.TK_CREATE); - reserved.put("date", LitebaseParser.TK_DATE); - reserved.put("datetime", LitebaseParser.TK_DATETIME); - reserved.put("day", LitebaseParser.TK_DAY); - reserved.put("default", LitebaseParser.TK_DEFAULT); - reserved.put("delete", LitebaseParser.TK_DELETE); - reserved.put("desc", LitebaseParser.TK_DESC); - reserved.put("distinct", LitebaseParser.TK_DISTINCT); - reserved.put("double", LitebaseParser.TK_DOUBLE); - reserved.put("drop", LitebaseParser.TK_DROP); - reserved.put("float", LitebaseParser.TK_FLOAT); - reserved.put("from", LitebaseParser.TK_FROM); - reserved.put("group", LitebaseParser.TK_GROUP); - reserved.put("having", LitebaseParser.TK_HAVING); - reserved.put("hour", LitebaseParser.TK_HOUR); - reserved.put("index", LitebaseParser.TK_INDEX); - reserved.put("insert", LitebaseParser.TK_INSERT); - reserved.put("int", LitebaseParser.TK_INT); - reserved.put("into", LitebaseParser.TK_INTO); - reserved.put("is", LitebaseParser.TK_IS); - reserved.put("key", LitebaseParser.TK_KEY); - reserved.put("like", LitebaseParser.TK_LIKE); - reserved.put("long", LitebaseParser.TK_LONG); - reserved.put("lower", LitebaseParser.TK_LOWER); - reserved.put("max", LitebaseParser.TK_MAX); - reserved.put("millis", LitebaseParser.TK_MILLIS); - reserved.put("min", LitebaseParser.TK_MIN); - reserved.put("minute", LitebaseParser.TK_MINUTE); - reserved.put("month", LitebaseParser.TK_MONTH); - reserved.put("nocase", LitebaseParser.TK_NOCASE); - reserved.put("not", LitebaseParser.TK_NOT); - reserved.put("null", LitebaseParser.TK_NULL); - reserved.put("on", LitebaseParser.TK_ON); - reserved.put("or", LitebaseParser.TK_OR); - reserved.put("order", LitebaseParser.TK_ORDER); - reserved.put("primary", LitebaseParser.TK_PRIMARY); - reserved.put("rename", LitebaseParser.TK_RENAME); - reserved.put("second", LitebaseParser.TK_SECOND); - reserved.put("select", LitebaseParser.TK_SELECT); - reserved.put("set", LitebaseParser.TK_SET); - reserved.put("short", LitebaseParser.TK_SHORT); - reserved.put("sum", LitebaseParser.TK_SUM); - reserved.put("table", LitebaseParser.TK_TABLE); - reserved.put("to", LitebaseParser.TK_TO); - reserved.put("update", LitebaseParser.TK_UPDATE); - reserved.put("upper", LitebaseParser.TK_UPPER); - reserved.put("values", LitebaseParser.TK_VALUES); - reserved.put("varchar", LitebaseParser.TK_VARCHAR); - reserved.put("where", LitebaseParser.TK_WHERE); - reserved.put("year", LitebaseParser.TK_YEAR); - } - - // juliana@224_2: improved memory usage on BlackBerry. - // juliana@253_9: improved Litebase parser. - /** - * The method which does the lexical analizys. - * - * @return The token itself or a code that represents it. - */ - int yylex() - { - int zzlen = zzReaderChars.length(), - yybefore, - initialPos; - int value; - - while (true) - { - while (yycurrent <= ' ') // Skips blanks - { - if (yyposition < zzlen) - yycurrent = zzReaderChars.charAt(yyposition++); - else - { - yycurrent = YYEOF; - break; - } - } - - if (yycurrent <= YYEOF) // Ends the lexical analysis if the end of the file is found. - return YYEOF; - - // Finds identifiers or reserved words. - if ((is[yycurrent] & IS_ALPHA) != 0) // The first character must be a letter. - { - int hashCode = 0; - - initialPos = yyposition - 1; - nameToken.setLength(0); // Initializes the current identifier token. - - while (yycurrent >= 0 && (is[yycurrent] & IS_ALPHA_DIGIT) != 0) // The other characters must be a letter, digit, or '_'. - { - if ('A' <= yycurrent && yycurrent <= 'Z') // Converts to lower case. - nameToken.append((char)(yycurrent += 32)); - else - nameToken.append((char)yycurrent); - hashCode = (hashCode << 5) - hashCode + yycurrent; - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - } - - // juliana@213_7: changed to Hashtable and tests for colision. - // Sees if the identifier is a reserved word or just an identifier. - if ((value = reserved.get(hashCode, nameToken)) != -1) - return value; - yyparser.yylval = nameToken.toString(); - return LitebaseParser.TK_IDENT; - } - - // Finds digits. - if ((is[yycurrent] & IS_START_DIGIT) != 0) // The start of a digit can be a digit or a sign. - { - if (yycurrent == '+') // juliana@226a_20: now passing +number (ex. +10) will work on sql clauses. - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - - initialPos = yyposition - 1; - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - - while (yycurrent >= 0 && (is[yycurrent] & IS_DIGIT) != 0) // The second part of the number must be digits. - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - - if (yycurrent == '.') // Tests if the number is not an integer. - { - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - - while (yycurrent >= 0 && (is[yycurrent] & IS_DIGIT) != 0) // Gets the rest of the digits of the non-integer number. - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - } - - // The number may finish with a letter indicating its type. - if (yycurrent >= 0 && (is[yycurrent] & IS_END_NUM) != 0) - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - - yyparser.yylval = zzReaderChars.substring(initialPos, yyposition - (yycurrent >= 0? 1 : 0)); - return LitebaseParser.TK_NUMBER; - } - - if ((is[yycurrent] & IS_RELATIONAL) != 0) // Finds tokens with two characters, or '>' or '<'. - { - yybefore = yycurrent; - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - if ((yybefore == '!' && yycurrent == '=') || (yybefore == '<' && yycurrent == '>')) - { - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - return LitebaseParser.TK_DIFF; // != or <>. - } - if (yybefore == '>' && yycurrent == '=') - { - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - return LitebaseParser.TK_GREATER_EQUAL; // >=. - } - if (yybefore == '<' && yycurrent == '=') - { - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - return LitebaseParser.TK_LESS_EQUAL; // <=. - } - if (yybefore == '>' || yybefore == '<') // > or <. - return yybefore; - - yyparser.yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - - // Finds tokens with one character, punctuators or '='. - // Sees if the tokens are arithmetic operators, '(', or ')'. In this case, returns the name of the token. - if ((is[yycurrent] & (IS_PUNCT | IS_OPERATOR)) != 0) - { - yybefore = yycurrent; - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - return yybefore; - } - - // juliana@225_6: a quote was not being correctly inserted in a string when not using prepared statements. - if (yycurrent == '\'') // Sees if the token is a string of the type 'xxx'. - { - nameToken.setLength(0); - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - initialPos = yyposition - 1; - - boolean needsNewString = false; - - while (yycurrent != '\'') - { - // juliana@238_3: the parser now accepts strings ending with \\. - if (yycurrent == '\\') // Sees if there is an escape in the string. - { - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - nameToken.append((char)yycurrent); - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - needsNewString = true; - } - else if (yycurrent == YYEOF) // The string must be closed before the end of the file. - yyparser.yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - else // Anything else can be inside the string. - { - nameToken.append((char)yycurrent); - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - } - } - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - - if (nameToken.length() == 0) - yyparser.yylval = ""; - else if (needsNewString) - yyparser.yylval = nameToken.toString(); - else - yyparser.yylval = zzReaderChars.substring(initialPos, yyposition - (yycurrent >= 0? 2 : 1)); - return LitebaseParser.TK_STR; - } - - // Error: invalid token. - yycurrent = (yyposition < zzlen)? zzReaderChars.charAt(yyposition++) : YYEOF; - yyparser.yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - } -} diff --git a/LitebaseSDK/src/java/litebase/LitebaseMessage.java b/LitebaseSDK/src/java/litebase/LitebaseMessage.java deleted file mode 100644 index f0af6f85e4..0000000000 --- a/LitebaseSDK/src/java/litebase/LitebaseMessage.java +++ /dev/null @@ -1,763 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@253_9: improved Litebase parser. - -/** - * Contains error messages with multi-language support. By now, only English and Portuguese are implemented. - */ -@Deprecated -class LitebaseMessage -{ - // General errors. - /** - * "Error: " - */ - static final int ERR_MESSAGE_START = 0; - - /** - * " Near position " - */ - static final int ERR_MESSAGE_POSITION = 1; - - /** - * "Syntax error." - */ - static final int ERR_SYNTAX_ERROR = 2; - - // Limit errors. - /** - * "Table name too big: must be <= 23." - */ - static final int ERR_MAX_TABLE_NAME_LENGTH = 3; - - /** - * "The maximum number of fields in a SELECT clause was exceeded." - */ - static final int ERR_FIELDS_OVERFLOW = 4; - - /** - * "The maximum number of columns was exceeded." - */ - static final int ERR_COLUMNS_OVERFLOW = 5; - - // Column errors. - /** - * "Unknown column " - */ - static final int ERR_UNKNOWN_COLUMN = 6; - - /** - * "Invalid column name: " - */ - static final int ERR_INVALID_COLUMN_NAME = 7; - - /** - * "Invalid column number: " - */ - static final int ERR_INVALID_COLUMN_NUMBER = 8; - - /** - * "The following column(s) does (do) not have an associated index " - */ - static final int ERR_COLUMN_DOESNOT_HAVE_AN_INDEX = 9; - - /** - * "Column name in field list is ambiguous: " - */ - static final int ERR_AMBIGUOUS_COLUMN_NAME = 10; - - /** - * "Column not found: " - */ - static final int ERR_COLUMN_NOT_FOUND = 11; - - /** - * "Duplicated column name: " - */ - static final int ERR_DUPLICATED_COLUMN_NAME = 12; - - // Primary key errors. - /** - * "A primary key was already defined for this table." - */ - static final int ERR_PRIMARY_KEY_ALREADY_DEFINED = 13; - - /** - * "Table does not have a primary key." - */ - static final int ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY = 14; - - /** - * "Statement creates a duplicated primary key in " - */ - static final int ERR_STATEMENT_CREATE_DUPLICATED_PK = 15; - - // Type errors. - /** - * "Incompatible types." - */ - static final int ERR_INCOMPATIBLE_TYPES = 16; - - /** - * "Field size must be a positive interger value." - */ - static final int ERR_FIELD_SIZE_IS_NOT_INT = 17; - - /** - * "The maximum size of CHAR or VARCHAR is 65535." - */ - static final int ERR_CHAR_TOO_BIG = 18; - - // Number of fields errors. - /** - * "The number of fields does not match the number of values " - */ - static final int ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH = 19; - - /** - * "The given number of values does not match the table definition." - */ - static final int ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION = 20; - - // Default value errors. - /** - * "Length of default value is bigger than column size." - */ - static final int ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER = 21; - - /** - * "An added column declared as NOT NULL must have a not null default value." - */ - static final int ERR_NOT_NULL_DEFAULT = 22; // juliana@253_22: added command ALTER TABLE ADD column. - - // Driver errors. - /** - * "This driver instance was closed and can't be used anymore. Please get a new instance of it." - */ - static final int ERR_DRIVER_CLOSED = 23; - - /** - * "ResultSet already closed!" - */ - static final int ERR_RESULTSET_CLOSED = 24; - - /** - * "RowIterator already closed!" - */ - static final int ERR_ROWITERATOR_CLOSED = 25; - - /** - * "ResultSetMetaData can't be used after the ResultSet is closed." - */ - static final int ERR_RESULTSETMETADATA_CLOSED = 26; - - /** - * "Cant't find native methods implementation for LitebaseConnection. Please install Litebase.dll/prc file." - */ - static final int ERR_LITEBASEPRCDLL_NOT_FOUND = 27; - - /** - * "The application id must be four characters long." - */ - static final int ERR_INVALID_CRID = 28; - - /** - * "The increment must be greater than 0 or -1." - */ - static final int ERR_INVALID_INC = 29; - - // juliana@253_11: now a DriverException will be throw if an incorrect parameter is passed in LitebaseConnection.getInstance(). - /** - * "Invalid connection parameter: " - */ - static final int ERR_INVALID_PARAMETER = 30; - - // juliana@253_20: added PreparedStatement.close(). - /** - * "The prepared statement is already closed." - */ - static final int ERR_PREPARED_CLOSED = 31; - - // Table errors. - /** - * "Table name not found: " - */ - static final int ERR_TABLE_NAME_NOT_FOUND = 32; - - /** - * "Table already created: " - */ - static final int ERR_TABLE_ALREADY_CREATED = 33; - - /** - * "It is not possible to open a table within a connection with a different string format." - */ - static final int ERR_WRONG_STRING_FORMAT = 34; // juliana@210_2: now Litebase supports tables with ascii strings. - - /** - * "It is not possible to open a table within a connection with a different cryptography format." - */ - static final int ERR_WRONG_CRYPTO_FORMAT = 35; // juliana@crypto_1: now Litebase supports weak cryptography. - - // ROWID error. - /** - * "ROWID can't be changed by the user!" - */ - static final int ERR_ROWID_CANNOT_BE_CHANGED = 36; - - // Prepared Statement errors. - /** - * "SQL statement does not return result set." - */ - static final int ERR_QUERY_DOESNOT_RETURN_RESULTSET = 37; - - /** - * "SQL statement does not perform updates in the database." - */ - static final int ERR_QUERY_DOESNOT_PERFORM_UPDATE = 38; - - /** - * "Not all parameters of the query had their values defined." - */ - static final int ERR_NOT_ALL_PARAMETERS_DEFINED = 39; - - /** - * "A value was not defined for the parameter " - */ - static final int ERR_PARAMETER_NOT_DEFINED = 40; - - /** - * "Invalid parameter index." - */ - static final int ERR_INVALID_PARAMETER_INDEX = 41; - - // Rename errors. - /** - * "Can't rename table. This table already exists: " - */ - static final int ERR_TABLE_ALREADY_EXIST = 42; - - /** - * "Column already exists: " - */ - static final int ERR_COLUMN_ALREADY_EXIST = 43; - - // Alias errors. - /** - * "Not unique table/alias: " - */ - static final int ERR_NOT_UNIQUE_ALIAS_TABLE = 44; - - /** - * "This alias is already being used in this expression: " - */ - static final int ERR_DUPLICATE_ALIAS = 45; - - /** - * "An alias is required for the aggregate function column." - */ - static final int ERR_REQUIRED_ALIAS = 46; - - // Litebase.execute() error. - /** - * "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute()." - */ - static final int ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED = 47; - - // Order by and group by errors. - /** - * "ORDER BY and GROUP BY clauses must match." - */ - static final int ERR_ORDER_GROUPBY_MUST_MATCH = 48; - - /** - * "No support for virtual columns in SQL queries with GROUP BY clause." - */ - static final int ERR_VIRTUAL_COLUMN_ON_GROUPBY = 49; - - // Function errors. - /** - * "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause." - */ - static final int ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT = 50; - - /** - * " is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as - * aliased aggregation functions." - */ - static final int ERR_IS_NOT_AGGREG_FUNCTION = 51; - - /** - * "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause." - */ - static final int ERR_CANNOT_MIX_AGGREG_FUNCTION = 52; - - /** - * "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause." - */ - static final int ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY = 53; - - /** - * " was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation - * funtions." - */ - static final int ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION = 54; - - /** - * "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields." - */ - static final int ERR_SUM_AVG_WITH_DATE_DATETIME = 55; - - // DATETIME error. - /** - * "Value is not a DATETIME: " - */ - static final int ERR_VALUE_ISNOT_DATETIME = 56; - - // Index errors. - /** - * "Index already created for column " - */ - static final int ERR_INDEX_ALREADY_CREATED = 57; - - /** - * "Can't drop a primary key index with drop index." - */ - static final int ERR_DROP_PRIMARY_KEY = 58; - - /** - * "Index too large. It can't have more than 65534 nodes." - */ - static final int ERR_INDEX_LARGE = 59; - - // NOT NULL errors. - /** - * "Primary key can't have null." - */ - static final int ERR_PK_CANT_BE_NULL = 60; - - /** - * "Field can't be null: " - */ - static final int ERR_FIELD_CANT_BE_NULL = 61; - - /** - * "A parameter in a where clause can't be null." - */ - static final int ERR_PARAM_NULL = 62; - - // Result set errors. - /** - * "ResultSet in invalid record position." - */ - static final int ERR_RS_INV_POS = 63; - - /** - * "Invalid value for decimal places: " - */ - static final int ERR_RS_DEC_PLACES_START = 64; - - /** - * ". Must be in the range -1 to 40." - */ - static final int ERR_RS_DEC_PLACES_END = 65; - - // File errors. - /** - * "Can't read from table." - */ - static final int ERR_CANT_READ = 66; - - /** - * "Can't load node: index corrupted." - */ - static final int ERR_CANT_LOAD_NODE = 67; - - /** - * "Table is corrupted: " - */ - static final int ERR_TABLE_CORRUPTED = 68; - - /** - * "Table not closed properly: " - */ - static final int ERR_TABLE_NOT_CLOSED = 69; // juliana@220_2 - - /** - * "A properly closed table can't be used in recoverTable(): " - */ - static final int ERR_TABLE_CLOSED = 70; // juliana@222_2 - - /** - * "Can't find index record position on delete." - */ - static final int ERR_IDX_RECORD_DEL = 71; - - /** - * "The table format is incompatible with Litebase version. Please update your tables." - */ - static final int ERR_WRONG_VERSION = 72; - - /** - * "The table format is not the previous one: " - */ - static final int ERR_WRONG_PREV_VERSION = 73; // juliana@220_11 - - /** - * "Invalid path: " - */ - static final int ERR_INVALID_PATH = 74; // juliana@214_1 - - /** - * "Database not found." - */ - static final int ERR_DB_NOT_FOUND = 75; // juliana@226_10 - - // BLOB errors. - /** - * "The total size of a blob can't be greater then 10 Mb." - */ - static final int ERR_BLOB_TOO_BIG = 76; - - /** - * "This is not a valid size multiplier." - */ - static final int ERR_INVALID_MULTIPLIER = 77; - - /** - * "A blob type can't be part of a primary key." - */ - static final int ERR_BLOB_PRIMARY_KEY = 78; - - /** - * "A BLOB column can't be indexed." - */ - static final int ERR_BLOB_INDEX = 79; - - /** - * "A BLOB can't be in the where clause." - */ - static final int ERR_BLOB_WHERE = 80; - - /** - * "A BLOB can't be converted to a string." - */ - static final int ERR_BLOB_STRING = 81; - - /** - * "Blobs types can't be in ORDER BY or GROUP BY clauses. - */ - static final int ERR_BLOB_ORDER_GROUP = 82; - - /** - * "It is not possible to compare BLOBs." - */ - static final int ERR_COMP_BLOBS = 83; - - /** - * "It is only possible to insert or update a BLOB through prepared statements." - */ - static final int ERR_BLOBS_PREPARED = 84; - - /** - * Total Litebase possible errors. - */ - static final int TOTAL_ERRORS = 85; - - // Error tables - private static final String[] errorMsgs_en = new String[TOTAL_ERRORS]; - private static final String[] errorMsgs_pt = new String[TOTAL_ERRORS]; - - static - { - // Some errors have space at the end. This can't be changed. - - // English messages. - // General errors. - errorMsgs_en[ERR_MESSAGE_START] = "Error: "; - errorMsgs_en[ERR_MESSAGE_POSITION] = " Near position "; - errorMsgs_en[ERR_SYNTAX_ERROR] = "Syntax error."; - - // Limit errors. - errorMsgs_en[ERR_MAX_TABLE_NAME_LENGTH] = "Table name too big: must be <= 23."; - errorMsgs_en[ERR_FIELDS_OVERFLOW] = "The maximum number of fields in a SELECT clause was exceeded."; - errorMsgs_en[ERR_COLUMNS_OVERFLOW] = "The maximum number of columns was exceeded."; - - // Column errors. - errorMsgs_en[ERR_UNKNOWN_COLUMN] = "Unknown column: "; - errorMsgs_en[ERR_INVALID_COLUMN_NAME] = "Invalid column name: "; - errorMsgs_en[ERR_INVALID_COLUMN_NUMBER] = "Invalid column number: "; - errorMsgs_en[ERR_COLUMN_DOESNOT_HAVE_AN_INDEX] = "The following column(s) does (do) not have an associated index "; - errorMsgs_en[ERR_AMBIGUOUS_COLUMN_NAME] = "Column name in field list is ambiguous: "; - errorMsgs_en[ERR_COLUMN_NOT_FOUND] = "Column not found: "; - errorMsgs_en[ERR_DUPLICATED_COLUMN_NAME] = "Duplicated column name "; - - // Primary key errors. - errorMsgs_en[ERR_PRIMARY_KEY_ALREADY_DEFINED] = "A primary key was already defined for this table."; - errorMsgs_en[ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY] = "Table does not have a primary key."; - errorMsgs_en[ERR_STATEMENT_CREATE_DUPLICATED_PK] = "Statement creates a duplicated primary key in "; - - // Type errors. - errorMsgs_en[ERR_INCOMPATIBLE_TYPES] = "Incompatible types"; - errorMsgs_en[ERR_FIELD_SIZE_IS_NOT_INT] = "Field size must be a positive interger value."; - errorMsgs_en[ERR_CHAR_TOO_BIG] = "The maximum size of CHAR or VARCHAR is 65535."; - - // Number of fields errors. - errorMsgs_en[ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH] = "The number of fields does not match the number of values "; - errorMsgs_en[ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION] = "The given number of values does not match the table definition."; - - // Default value errors. - errorMsgs_en[ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER] = "Length of default value is bigger than column size."; - errorMsgs_en[ERR_NOT_NULL_DEFAULT] = "An added column declared as NOT NULL must have a not null default value."; - - // Driver errors. - errorMsgs_en[ERR_DRIVER_CLOSED] = "This driver instance was closed and can't be used anymore. Please get a new instance of it."; - errorMsgs_en[ERR_RESULTSET_CLOSED] = "ResultSet already closed!"; - errorMsgs_en[ERR_ROWITERATOR_CLOSED] = "RowIterator already closed!"; - errorMsgs_en[ERR_RESULTSETMETADATA_CLOSED] = "ResultSetMetaData can't be used after the ResultSet is closed."; - errorMsgs_en[ERR_LITEBASEPRCDLL_NOT_FOUND] = "Can't find native methods implementation for LitebaseConnection. Please install Litebase.dll/prc " - + "file."; - errorMsgs_en[ERR_INVALID_CRID] = "The application id must be 4 characters long."; - errorMsgs_en[ERR_INVALID_INC] = "The increment must be greater than 0 or -1."; - errorMsgs_en[ERR_INVALID_PARAMETER] = "Invalid connection parameter: "; - errorMsgs_en[ERR_PREPARED_CLOSED] = "The prepared statement is already closed."; - - // Table errors. - errorMsgs_en[ERR_TABLE_NAME_NOT_FOUND] = "Table name not found: "; - errorMsgs_en[ERR_TABLE_ALREADY_CREATED] = "Table already created: "; - errorMsgs_en[ERR_WRONG_STRING_FORMAT] = "It is not possible to open a table within a connection with a different string format."; - errorMsgs_en[ERR_WRONG_CRYPTO_FORMAT] = "It is not possible to open a table within a connection with a different cryptography format."; - - // ROWID error. - errorMsgs_en[ERR_ROWID_CANNOT_BE_CHANGED] = "ROWID can't be changed by the user!"; - - // Prepared Statement errors. - errorMsgs_en[ERR_QUERY_DOESNOT_RETURN_RESULTSET] = "SQL statement does not return result set."; - errorMsgs_en[ERR_QUERY_DOESNOT_PERFORM_UPDATE] = "SQL statement does not perform updates in the database."; - errorMsgs_en[ERR_NOT_ALL_PARAMETERS_DEFINED] = "Not all parameters of the query had their values defined."; - errorMsgs_en[ERR_PARAMETER_NOT_DEFINED] = "A value was not defined for the parameter "; - errorMsgs_en[ERR_INVALID_PARAMETER_INDEX] = "Invalid parameter index."; - - // Rename errors. - errorMsgs_en[ERR_TABLE_ALREADY_EXIST] = "Can't rename table. This table already exists: "; - errorMsgs_en[ERR_COLUMN_ALREADY_EXIST] = "Column already exists: "; - - // Alias errors. - errorMsgs_en[ERR_NOT_UNIQUE_ALIAS_TABLE] = "Not unique table/alias: "; - errorMsgs_en[ERR_DUPLICATE_ALIAS] = "This alias is already being used in this expression: "; - errorMsgs_en[ERR_REQUIRED_ALIAS] = "An alias is required for the aggregate function column."; - - // Litebase.execute() error. - errorMsgs_en[ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED] = "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute()."; - - // Order by and group by errors. - errorMsgs_en[ERR_ORDER_GROUPBY_MUST_MATCH] = "ORDER BY and GROUP BY clauses must match."; - errorMsgs_en[ERR_VIRTUAL_COLUMN_ON_GROUPBY] = "No support for virtual columns in SQL queries with GROUP BY clause."; - - // Function errors. - errorMsgs_en[ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT] = "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY " - + "clause."; - errorMsgs_en[ERR_IS_NOT_AGGREG_FUNCTION] = " is not an aggregation function. All fields present in a HAVING " - + "clause must be listed in the SELECT clause as aliased aggregation funtions."; - errorMsgs_en[ERR_CANNOT_MIX_AGGREG_FUNCTION] = "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY " - + "clause."; - errorMsgs_en[ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY] = "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause."; - errorMsgs_en[ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION] = " was not listed in the SELECT clause. All fields present in a HAVING " - + "clause must be listed in the SELECT clause as aliased aggregation funtions."; - errorMsgs_en[ERR_SUM_AVG_WITH_DATE_DATETIME] = "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields."; - - // DATETIME error. - errorMsgs_en[ERR_VALUE_ISNOT_DATETIME] = "Value is not a DATETIME: "; - - // Index error. - errorMsgs_en[ERR_INDEX_ALREADY_CREATED] = "Index already created for column "; - errorMsgs_en[ERR_DROP_PRIMARY_KEY] = "Can't drop a primary key index with drop index."; - errorMsgs_en[ERR_INDEX_LARGE] = "Index too large. It can't have more than 65534 nodes."; - - // NOT NULL errors. - errorMsgs_en[ERR_PK_CANT_BE_NULL] = "Primary key can't have null."; - errorMsgs_en[ERR_FIELD_CANT_BE_NULL] = "Field can't be null: "; - errorMsgs_en[ERR_PARAM_NULL] = "A parameter in a where clause can't be null."; - - // Result set errors. - errorMsgs_en[ERR_RS_INV_POS] = "ResultSet in invalid record position."; - errorMsgs_en[ERR_RS_DEC_PLACES_START] = "Invalid value for decimal places: "; - errorMsgs_en[ERR_RS_DEC_PLACES_END] = ". Must be in the range -1 to 40."; - - // File errors. - errorMsgs_en[ERR_CANT_READ] = "Can't read from table."; - errorMsgs_en[ERR_CANT_LOAD_NODE] = "Can't load node: index corrupted."; - errorMsgs_en[ERR_TABLE_CORRUPTED] = "Table is corrupted: "; - errorMsgs_en[ERR_TABLE_NOT_CLOSED] = "Table not closed properly: "; - errorMsgs_en[ERR_TABLE_CLOSED] = "A properly closed table can't be used in recoverTable(): "; // juliana@222_2 - errorMsgs_en[ERR_IDX_RECORD_DEL] = "Can't find index record position on delete."; - errorMsgs_en[ERR_WRONG_VERSION] = "The table format is incompatible with Litebase version. Please update your tables."; - errorMsgs_en[ERR_WRONG_PREV_VERSION] = "The table format is not the previous one: "; // juliana@220_11 - errorMsgs_en[ERR_INVALID_PATH] = "Invalid path: "; // juliana@214_1 - errorMsgs_en[ERR_DB_NOT_FOUND] = "Database not found."; // juliana@226_10 - - // BLOB errors. - errorMsgs_en[ERR_BLOB_TOO_BIG] = "The total size of a blob can't be greater then 10 Mb."; - errorMsgs_en[ERR_INVALID_MULTIPLIER] = "This is not a valid size multiplier."; - errorMsgs_en[ERR_BLOB_PRIMARY_KEY] = "A blob type can't be part of a primary key."; - errorMsgs_en[ERR_BLOB_INDEX] = "A BLOB column can't be indexed."; - errorMsgs_en[ERR_BLOB_WHERE] = "A BLOB can't be in the where clause."; - errorMsgs_en[ERR_BLOB_STRING] = "A BLOB can't be converted to a string."; - errorMsgs_en[ERR_BLOB_ORDER_GROUP] = "Blobs types can't be in ORDER BY or GROUP BY clauses."; - errorMsgs_en[ERR_COMP_BLOBS] = "It is not possible to compare BLOBs."; - errorMsgs_en[ERR_BLOBS_PREPARED] = "It is only possible to insert or update a BLOB through prepared statements using setBlob()."; - - // Portuguese messages. - // General errors. - errorMsgs_pt[ERR_MESSAGE_START] = "Erro: "; - errorMsgs_pt[ERR_MESSAGE_POSITION] = ". Pr�ximo � posi��o "; - errorMsgs_pt[ERR_SYNTAX_ERROR] = "Erro de sintaxe."; - - // Limit errors. - errorMsgs_pt[ERR_MAX_TABLE_NAME_LENGTH] = "Nome da tabela muito grande: deve ser <= 23."; - errorMsgs_pt[ERR_FIELDS_OVERFLOW] = "O n�mero m�ximo de campos na cl�usula SELECT foi excedido."; - errorMsgs_pt[ERR_COLUMNS_OVERFLOW] = "O n�mero m�ximo de colunas foi excedido."; - - // Column errors. - errorMsgs_pt[ERR_UNKNOWN_COLUMN] = "Coluna desconhecida "; - errorMsgs_pt[ERR_INVALID_COLUMN_NAME] = "Nome de coluna inv�lido: "; - errorMsgs_pt[ERR_INVALID_COLUMN_NUMBER] = "N�mero de coluna inv�lido: "; - errorMsgs_pt[ERR_COLUMN_DOESNOT_HAVE_AN_INDEX] = "A(s) coluna(s) a seguir n�o tem (t�m) um ind�ce associado "; - errorMsgs_pt[ERR_AMBIGUOUS_COLUMN_NAME] = "Nome de coluna amb�guo: "; - errorMsgs_pt[ERR_COLUMN_NOT_FOUND] = "Coluna n�o encontrada: "; - errorMsgs_pt[ERR_DUPLICATED_COLUMN_NAME] = "Nome de coluna duplicado "; - - // Primary key errors. - errorMsgs_pt[ERR_PRIMARY_KEY_ALREADY_DEFINED] = "Uma chave primaria j� foi definida para esta tabela."; - errorMsgs_pt[ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY] = "Tabela n�o tem chave prim�ria."; - errorMsgs_pt[ERR_STATEMENT_CREATE_DUPLICATED_PK] = "Comando cria uma chave prim�ria duplicada em "; - - // Type errors. - errorMsgs_pt[ERR_INCOMPATIBLE_TYPES] = "Tipos incompat�veis"; - errorMsgs_pt[ERR_FIELD_SIZE_IS_NOT_INT] = "Tamanho do campo deve ser um valor inteiro positivo."; - errorMsgs_pt[ERR_CHAR_TOO_BIG] = "O tamanho m�ximo de um CHAR ou VARCHAR � 65535."; - - // Number of fields errors. - errorMsgs_pt[ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH] = "O n�mero de campos � diferente do n�mero de valores "; - errorMsgs_pt[ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION] = "O n�mero de valores dado n�o coincide com a defini��o da tabela."; - - // Default value errors. - errorMsgs_pt[ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER] = "Tamanho do valor padr�o � maior que o tamanho definido para a coluna."; - errorMsgs_pt[ERR_NOT_NULL_DEFAULT] = "Uma coluna adicionada declarada como NOT NULL deve ter um valor padr�o n�o nulo."; - - // Driver errors. - errorMsgs_pt[ERR_DRIVER_CLOSED] = "Esta inst�ncia do driver est� fechada e n�o pode ser mais utilizada. Por favor, obtenha uma nova " - + "inst�ncia."; - errorMsgs_pt[ERR_RESULTSET_CLOSED] = "ResultSet j� est� fechado!"; - errorMsgs_pt[ERR_ROWITERATOR_CLOSED] = "RowIterator j� est� fechado!"; - errorMsgs_pt[ERR_RESULTSETMETADATA_CLOSED] = "ResultSetMetaData n�o pode ser usado depois que o ResultSet estiver fechado."; - errorMsgs_pt[ERR_LITEBASEPRCDLL_NOT_FOUND] = "N�o � poss�vel encontrar a implementa��o dos m�todos nativos " - + "para o LitebaseConnection. Por favor, instale o arquivo Litebase.dll/prc."; - errorMsgs_pt[ERR_INVALID_CRID] = "O id da aplica��o de ter 4 characteres."; - errorMsgs_pt[ERR_INVALID_INC] = "O incremento deve ser maior do que 0 ou -1."; - errorMsgs_pt[ERR_INVALID_PARAMETER] = "Par�metro de conex�o inv�lido: "; - errorMsgs_pt[ERR_PREPARED_CLOSED] = "O prepared statement j� est� fechado."; - - // Table errors. - errorMsgs_pt[ERR_TABLE_NAME_NOT_FOUND] = "Nome da tabela n�o encontrado: "; - errorMsgs_pt[ERR_TABLE_ALREADY_CREATED] = "Tabela j� existe: "; - errorMsgs_pt[ERR_WRONG_STRING_FORMAT] = "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de strings diferente."; - errorMsgs_pt[ERR_WRONG_CRYPTO_FORMAT] = "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de criptografia diferente."; - - // ROWID error. - errorMsgs_pt[ERR_ROWID_CANNOT_BE_CHANGED] = "ROWID n�o pode ser mudado pelo usu�rio!"; - - // Prepared Statement errors. - errorMsgs_pt[ERR_QUERY_DOESNOT_RETURN_RESULTSET] = "Comando SQL n�o retorna um ResultSet."; - errorMsgs_pt[ERR_QUERY_DOESNOT_PERFORM_UPDATE] = "Comando SQL n�o executa uma atualiza��o no banco de dados."; - errorMsgs_pt[ERR_NOT_ALL_PARAMETERS_DEFINED] = "Nem todos os par�metros da consulta tiveram seus valores definidos."; - errorMsgs_pt[ERR_PARAMETER_NOT_DEFINED] = "N�o foi definido um valor para o par�metro "; - errorMsgs_pt[ERR_INVALID_PARAMETER_INDEX] = "�ndice de par�metro inv�lido."; - - // Rename errors. - errorMsgs_pt[ERR_TABLE_ALREADY_EXIST] = "N�o � poss�vel renomear a tabela. Esta tabela j� existe: "; - errorMsgs_pt[ERR_COLUMN_ALREADY_EXIST] = "Coluna j� existe: "; - - // Alias errors. - errorMsgs_pt[ERR_NOT_UNIQUE_ALIAS_TABLE] = "Nome de tabela/alias repetido: "; - errorMsgs_pt[ERR_DUPLICATE_ALIAS] = "Este alias j� est� sendo utilizado no sql: "; - errorMsgs_pt[ERR_REQUIRED_ALIAS] = "Um alias � necess�rio para colunas com fun��o de agrega��o."; - - // Litebase.execute() error. - errorMsgs_pt[ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED] = "Apenas CREATE TABLE e CREATE INDEX s�o permitidos no Litebase.execute()."; - - // Order by and group by errors. - errorMsgs_pt[ERR_ORDER_GROUPBY_MUST_MATCH] = "Cl�usulas ORDER BY e GROUP BY devem coincidir."; - errorMsgs_pt[ERR_VIRTUAL_COLUMN_ON_GROUPBY] = "SQL com cl�usula GROUP BY n�o tem suporte para colunas virtuais."; - - // Function errors. - errorMsgs_pt[ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT] = "Todas colunas que n�os�o fun��es de " - + "agrega��o na cl�usula SELECT devem estar na cl�usula GROUP BY."; - errorMsgs_pt[ERR_IS_NOT_AGGREG_FUNCTION] = " n�o � uma fun��o de agrega��o. Todos as colunas " - + "da cl�usula HAVING devem ser listadas no SELECT utilizando alias."; - errorMsgs_pt[ERR_CANNOT_MIX_AGGREG_FUNCTION] = "N�o � possivel misturar colunas reais e de agrega��o no SELECT sem cl�usula GROUP BY."; - errorMsgs_pt[ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY] = "N�o � poss�vel ter fun��es de agrega��o com cl�usula ORDER BY sem cl�usula GROUP BY."; - errorMsgs_pt[ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION] = " n�o foi listado no SELECT. Todas " - + "as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."; - errorMsgs_pt[ERR_SUM_AVG_WITH_DATE_DATETIME] = "Fun��es de agrega��o SUM e AVG n�o s�o usadas com colunas do tipo DATE e DATETIME."; - - // DATETIME error. - errorMsgs_pt[ERR_VALUE_ISNOT_DATETIME] = "Valor n�o � um tipo DATETIME v�lido: "; - - // Index error. - errorMsgs_pt[ERR_INDEX_ALREADY_CREATED] = "�ndice j� criado para a coluna "; - errorMsgs_pt[ERR_DROP_PRIMARY_KEY] = "N�o � poss�vel remover uma chave prim�ria usando drop index."; - errorMsgs_pt[ERR_INDEX_LARGE] = "�ndice muito grande. Ele n�o pode ter mais do que 65534 n�s."; - - // NOT NULL errors. - errorMsgs_pt[ERR_PK_CANT_BE_NULL] = "Chave prim�ria n�o pode ter NULL."; - errorMsgs_pt[ERR_FIELD_CANT_BE_NULL] = "Coluna n�o pode ser NULL: "; - errorMsgs_pt[ERR_PARAM_NULL] = "Um par�metro em uma where clause n�o pode ser NULL."; - - // Result set errors. - errorMsgs_pt[ERR_RS_INV_POS] = "ResultSet em uma posi��o de registro inv�lida."; - errorMsgs_pt[ERR_RS_DEC_PLACES_START] = "Valor inv�lido para casas decimais: "; - errorMsgs_pt[ERR_RS_DEC_PLACES_END] = ". Deve ficar entre - 1 e 40."; - - // File errors. - errorMsgs_pt[ERR_CANT_READ] = "N�o � poss�vel ler da tabela."; - errorMsgs_pt[ERR_CANT_LOAD_NODE] = "N�o � poss�vel carregar o n�: �ndice corrompido."; - errorMsgs_pt[ERR_TABLE_CORRUPTED] = "Tabela est� corrompida: "; - errorMsgs_pt[ERR_TABLE_NOT_CLOSED] = "Tabela n�o foi fechada corretamente: "; - errorMsgs_pt[ERR_TABLE_CLOSED] = "Uma tabela fechada corretamente n�o pode ser usada no recoverTable(): "; // juliana@222_2 - errorMsgs_pt[ERR_IDX_RECORD_DEL] = "N�o � poss�vel achar a posi��o de registro no �ndice na exclus�o."; - errorMsgs_pt[ERR_WRONG_VERSION] = "O formato de tabela n�o � compat�vel com a vers�o do Litebase. Por favor, atualize suas tabelas."; - errorMsgs_pt[ERR_WRONG_PREV_VERSION] = "O formato de tabela n�o � o anterior: "; // juliana@220_11 - errorMsgs_pt[ERR_INVALID_PATH] = "Caminho inv�lido: "; // juliana@214_1 - errorMsgs_pt[ERR_DB_NOT_FOUND] = "Base de dados n�o encontrada."; // juliana@226_10 - - // BLOB errors. - errorMsgs_pt[ERR_BLOB_TOO_BIG] = "O tamanho total de um BLOB n�o pode ser maior do que 10 Mb."; - errorMsgs_pt[ERR_INVALID_MULTIPLIER] = "O multiplicador de tamanho n�o � v�lido."; - errorMsgs_pt[ERR_BLOB_PRIMARY_KEY] = "Um tipo BLOB n�o pode ser parte de uma chave prim�ria."; - errorMsgs_pt[ERR_BLOB_INDEX] = "Uma coluna do tipo BLOB n�o pode ser indexada."; - errorMsgs_pt[ERR_BLOB_WHERE] = "Um BLOB n�o pode estar na cl�usula WHERE."; - errorMsgs_pt[ERR_BLOB_STRING] = "Um BLOB n�o pode ser convertido em uma string."; - errorMsgs_pt[ERR_BLOB_ORDER_GROUP] = "Tipos BLOB n�o podem estar em cl�usulas ORDER BY ou GROUP BY."; - errorMsgs_pt[ERR_COMP_BLOBS] = "N�o � poss�vel comparar BLOBs."; - errorMsgs_pt[ERR_BLOBS_PREPARED] = "S� � poss�vel inserir ou atualizar um BLOB atrav�s prepared statements usando setBlob()."; - } - - /** - * Gets the correct error message. - * - * @param messageNumber The error message code. - * @return The string with the desired error message. - */ - static String getMessage(int messageNumber) - { - if (LitebaseConnection.language == LitebaseConnection.LANGUAGE_PT) - return errorMsgs_pt[messageNumber]; - return errorMsgs_en[messageNumber]; - } -} diff --git a/LitebaseSDK/src/java/litebase/LitebaseParser.java b/LitebaseSDK/src/java/litebase/LitebaseParser.java deleted file mode 100644 index 31008e676d..0000000000 --- a/LitebaseSDK/src/java/litebase/LitebaseParser.java +++ /dev/null @@ -1,1786 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.*; -import totalcross.util.*; - -// juliana@253_9: improved Litebase parser. - -/** - * This class calls yyparse() and builds the parser result. - */ -@Deprecated -class LitebaseParser -{ - /** - * CHAR keyword token. - */ - final static int TK_CHAR = 0; - - /** - * SHORT keyword token. - */ - final static int TK_SHORT = 1; - - /** - * INT keyword token. - */ - final static int TK_INT = 2; - - /** - * LONG keyword token. - */ - final static int TK_LONG = 3; - - /** - * FLOAT keyword token. - */ - final static int TK_FLOAT = 4; - - /** - * DOUBLE keyword token. - */ - final static int TK_DOUBLE = 5; - - /** - * VARCHAR keyword token. - */ - final static int TK_VARCHAR = 6; - - /** - * NOCASE keyword token. - */ - final static int TK_NOCASE = 7; - - /** - * DATE keyword token. - */ - final static int TK_DATE = 8; - - /** - * DATETIME keyword token. - */ - final static int TK_DATETIME = 9; - - /** - * BLOB keyword token. - */ - final static int TK_BLOB = 10; - - /** - * ABS keyword token. - */ - final static int TK_ABS = 11; - - /** - * ADD keyword token. - */ - final static int TK_ADD = 12; - - /** - * ALTER keyword token. - */ - final static int TK_ALTER = 13; - - /** - * AND keyword token. - */ - final static int TK_AND = 14; - - /** - * AS keyword token. - */ - final static int TK_AS = 15; - - /** - * ASC keyword token. - */ - final static int TK_ASC = 16; - - /** - * AVG keyword token. - */ - final static int TK_AVG = 17; - - /** - * BY keyword token. - */ - final static int TK_BY = 18; - - /** - * COUNT keyword token. - */ - final static int TK_COUNT = 19; - - /** - * CREATE keyword token. - */ - final static int TK_CREATE = 20; - - /** - * DAY keyword token. - */ - final static int TK_DAY = 21; - - /** - * DEFAULT keyword token. - */ - final static int TK_DEFAULT = 22; - - /** - * DELETE keyword token. - */ - final static int TK_DELETE = 23; - - /** - * DESC keyword token. - */ - final static int TK_DESC = 24; - - /** - * DISTINCT keyword token. - */ - final static int TK_DISTINCT = 25; - - /** - * DROP keyword token. - */ - final static int TK_DROP = 26; - - /** - * FROM keyword token. - */ - final static int TK_FROM = 27; - - /** - * GROUP keyword token. - */ - final static int TK_GROUP = 28; - - /** - * HAVING keyword token. - */ - final static int TK_HAVING = 29; - - /** - * HOUR keyword token. - */ - final static int TK_HOUR = 30; - - /** - * INDEX keyword token. - */ - final static int TK_INDEX = 31; - - /** - * INSERT keyword token. - */ - final static int TK_INSERT = 32; - - /** - * INTO keyword token. - */ - final static int TK_INTO = 33; - - /** - * IS keyword token. - */ - final static int TK_IS = 34; - - /** - * KEY keyword token. - */ - final static int TK_KEY = 35; - - /** - * LIKE keyword token. - */ - final static int TK_LIKE = 36; - - /** - * LOWER keyword token. - */ - final static int TK_LOWER = 37; - - /** - * MAX keyword token. - */ - final static int TK_MAX = 38; - - /** - * MILLIS keyword token. - */ - final static int TK_MILLIS = 39; - - /** - * '(' token. - */ - final static int TK_OPEN = 40; - - /** - * ')' token. - */ - final static int TK_CLOSE = 41; - - /** - * '*' token. - */ - final static int TK_ASTERISK = 42; - - /** - * MIN keyword token. - */ - final static int TK_MIN = 43; - - /** - * ',' token. - */ - final static int TK_COMMA = 44; - - /** - * MINUTE keyword token. - */ - final static int TK_MINUTE = 45; - - /** - * '.' token. - */ - final static int TK_DOT = 46; - - /** - * MONTH keyword token. - */ - final static int TK_MONTH = 47; - - /** - * NOT keyword token. - */ - final static int TK_NOT = 48; - - /** - * NULL keyword token. - */ - final static int TK_NULL = 49; - - /** - * ON keyword token. - */ - final static int TK_ON = 50; - - /** - * OR keyword token. - */ - final static int TK_OR = 51; - - /** - * ORDER keyword token. - */ - final static int TK_ORDER = 52; - - /** - * PRIMARY keyword token. - */ - final static int TK_PRIMARY = 53; - - /** - * RENAME keyword token. - */ - final static int TK_RENAME = 54; - - /** - * SECOND keyword token. - */ - final static int TK_SECOND = 55; - - /** - * SELECY keyword token. - */ - final static int TK_SELECT = 56; - - /** - * SET keyword token. - */ - final static int TK_SET = 57; - - /** - * SUM keyword token. - */ - final static int TK_SUM = 58; - - /** - * TABLE keyword token. - */ - final static int TK_TABLE = 59; - - /** - * '<' token. - */ - final static int TK_LESS = 60; - - /** - * '=' token. - */ - final static int TK_EQUAL = 61; - - /** - * '>' token. - */ - final static int TK_GREATER = 62; - - /** - * '?' token. - */ - final static int TK_INTERROGATION = 63; - - /** - * TO keyword token. - */ - final static int TK_TO = 64; - - /** - * UPPER keyword token. - */ - final static int TK_UPPER = 65; - - /** - * VALUES keyword token. - */ - final static int TK_VALUES = 66; - - /** - * UPDATE keyword token. - */ - final static int TK_UPDATE = 67; - - /** - * WHERE keyword token. - */ - final static int TK_WHERE = 68; - - /** - * YEAR keyword token. - */ - final static int TK_YEAR = 69; - - /** - * Identifier token. - */ - final static int TK_IDENT = 70; - - /** - * Numerical token. - */ - final static int TK_NUMBER = 71; - - /** - * String token. - */ - final static int TK_STR = 72; - - /** - * '>=' token. - */ - final static int TK_GREATER_EQUAL = 73; - - /** - * '<=' token. - */ - final static int TK_LESS_EQUAL = 74; - - /** - * '!=' or '<>' token. - */ - final static int TK_DIFF = 75; - - /** - * The 'lval' (result) got from yylex(). - */ - String yylval; - - /** - * The first table name found in an update statement. - */ - private String firstFieldUpdateTableName; - - /** - * The first table alias found in an update statement. - */ - private String firstFieldUpdateAlias; - - /** - * The second table name found in an update statement, which indicates an error. - */ - private String secondFieldUpdateTableName; - - /** - * The second table alias found in an update statement, which indicates an error. - */ - private String secondFieldUpdateAlias; - - /** - * The lexical analyzer. - */ - private LitebaseLex lexer; - - /** - * The type of SQL command, which can be one of: CMD_CREATE_TABLE, CMD_CREATE_INDEX, - * CMD_DROP_TABLE, CMD_DROP_INDEX, CMD_ALTER_DROP_PK, - * CMD_ALTER_ADD_PK, CMD_ALTER_RENAME_TABLE, CMD_ALTER_RENAME_COLUMN, - * CMD_SELECT, CMD_INSERT, CMD_UPDATE, or CMD_DELETE. - */ - int command; - - /** - * The number of fields in the field list. - */ - int fieldListSize; - - /** - * The number of fields of values in the field values list. - */ - int fieldValuesSize; - - /** - * The number of fields of the update field list. - */ - int fieldNamesSize; - - /** - * The number of tables in the table list. - */ - int tableListSize; - - /** - * Counts the number of simple primary keys, which must be only one. - */ - private int number_pk; - - /** - * This is used to differ between a where clause and a having clause. Before parsing the having clause, isWhereClause is set to - * false. So the getInstanceBooleanClause() method will return a having clause, otherwise it returns a where clause. - */ - boolean isWhereClause = true; - - /** - * Contains field values (strings) used on insert/update statements. - */ - String[] fieldValues; - - /** - * The field list for inserts, updates and indices. - */ - String[] fieldNames; - - /** - * The resulting set table list, used with all statements. - */ - SQLResultSetTable[] tableList; - - /** - * The field list for the SQL commands except SELECT. - */ - SQLFieldDefinition[] fieldList; - - /** - * The where clause of a SELECT statement. - */ - SQLBooleanClause whereClause; - - /** - * The having clause of a SELECT statement. - */ - SQLBooleanClause havingClause; - - /** - * An auxiliary expression tree. - */ - SQLBooleanClauseTree auxTree; - - /** - * An auxiliary field. - */ - SQLResultSetField auxField; - - /** - * The initial part of the SELECT statement - */ - SQLSelectClause select; - - /** - * The order by part of a SELECT statement. - */ - SQLColumnListClause orderBy; - - /** - * The group by part of a SELECT statement. - */ - SQLColumnListClause groupBy; - - /** - * The lex main method. - * - * @return The token code, -1 if the end of file was reached or 256 if there was a lexical error. - */ - private int yylex() - { - return lexer.yylex(); - } - - /** - * The method which executes the parser process. - * - * @param sql The sql command to be parsed. - * @param parser The parser object which will be filled with the result of the parsing process. - * @param lexer The lexical analyzer. - * @throws InvalidNumberException If an internal method throws it. - */ - static void parser(String sql, LitebaseParser parser, LitebaseLex lexer) throws InvalidNumberException - { - LitebaseParser yyparser = parser; // Initializes the parser. - - // juliana@224_2: improved memory usage on BlackBerry. - yyparser.lexer = lexer; - lexer.zzReaderChars = sql; - lexer.yyparser = parser; - lexer.yycurrent = ' '; - lexer.yyposition = 0; - - yyparser.yyparse(); - } - - /** - * Gets an instance of a where clause or a having clause, depending of what clause is used in the SELECT statement. - * - * @return The instance of a where clause or a having clause. - */ - SQLBooleanClause getInstanceBooleanClause() - { - if (isWhereClause) // where clause - { - if (whereClause == null) - whereClause = new SQLBooleanClause(); - return whereClause; - } - else // having clause - { - SQLBooleanClause having = havingClause; - if (having == null) - having = havingClause = new SQLBooleanClause(); - having.isWhereClause = false; - return having; - } - } - - /** - * Gets an instance of an order by clause used in the SELECT statement. - * - * @return The order by clause. - */ - SQLColumnListClause getInstanceColumnListClauseOrderBy() - { - if (orderBy == null) - orderBy = new SQLColumnListClause(); - return orderBy; - } - - /** - * Gets an instance of a group by clause used in the SELECT statement. - * - * @return The group by clause. - */ - SQLColumnListClause getInstanceColumnListClauseGroupBy() - { - if (groupBy == null) - groupBy = new SQLColumnListClause(); - return groupBy; - } - - /** - * Parses input and execute indicated items. - * @throws InvalidNumberException If an internal method throws it. - */ - private void yyparse() throws InvalidNumberException - { - int token = LitebaseLex.YYEOF; - String tableName; - - switch (yylex()) - { - case TK_ALTER: // Alter table. - if (yylex() != TK_TABLE || yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableList[0] = new SQLResultSetTable(yylval); // There's no alias table name here. - - switch (yylex()) - { - case TK_ADD: // Adds a primary key or a new column. - - // juliana@253_22: added command ALTER TABLE ADD column. - if ((token = createColumn()) == TK_PRIMARY) // Adds a primary key. - { - if (fieldListSize == 1) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - if (yylex() != TK_KEY || yylex() != TK_OPEN || colnameCommaList() != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - command = SQLElement.CMD_ALTER_ADD_PK; - } - else if (token == -1) // Adds a new column. - { - SQLFieldDefinition field = fieldList[0]; - if (field.isNotNull && field.defaultValue == null) // A field declared as not null must have a default value. - yyerror(LitebaseMessage.ERR_NOT_NULL_DEFAULT); - if (field.isPrimaryKey) // The new field can't be declared as a primary key when being added. - { - if (field.defaultValue != null) // All the keys would be the same. - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_STATEMENT_CREATE_DUPLICATED_PK) - + tableList[0].tableName); - - yyerror(LitebaseMessage.ERR_PK_CANT_BE_NULL); // All the keys would be null. - } - command = SQLElement.CMD_ALTER_ADD_COLUMN; - } - - break; - - case TK_DROP: // Drops a primary key. - if (yylex() != TK_PRIMARY || yylex() != TK_KEY) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - command = SQLElement.CMD_ALTER_DROP_PK; - break; - - case TK_RENAME: // Renames the table or a column. - if ((token = yylex()) == TK_IDENT) // Rename column. - { - command = SQLElement.CMD_ALTER_RENAME_COLUMN; - fieldNames[1] = yylval; - token = yylex(); - } - else // Rename table. - command = SQLElement.CMD_ALTER_RENAME_TABLE; - - // New name. - if (token != TK_TO || yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - fieldNames[0] = yylval; - - break; - - default: - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - - token = yylex(); - break; - - case TK_CREATE: - { - switch (yylex()) - { - case TK_TABLE: // Create table. - if (yylex() != TK_IDENT || yylex() != TK_OPEN) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableList[0] = new SQLResultSetTable(yylval); // There's no alias table name here. - - // Primary key. - if ((token = createColumnCommalist()) == TK_PRIMARY && yylex() == TK_KEY && yylex() == TK_OPEN && colnameCommaList() == TK_CLOSE) - { - if (number_pk == 1) - yyerror(LitebaseMessage.ERR_PRIMARY_KEY_ALREADY_DEFINED); - token = yylex(); - } - - if (token != TK_CLOSE) // End of create table. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - command = SQLElement.CMD_CREATE_TABLE; - break; - - case TK_INDEX: // Create index. - if (yylex() != TK_IDENT || yylex() != TK_ON || yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableList[0] = new SQLResultSetTable(yylval); // There's no alias table name here. - if (yylex() != TK_OPEN || colnameCommaList() != TK_CLOSE) // Column name list. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - command = SQLElement.CMD_CREATE_INDEX; - break; - - default: - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - - token = yylex(); - break; - } - case TK_DELETE: // Delete. - if (!(((token = yylex()) == TK_FROM && yylex() == TK_IDENT) || token == TK_IDENT)) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableName = yylval; - - if ((token = yylex()) == TK_IDENT) // Alias table name. - { - tableList[0] = new SQLResultSetTable(tableName, yylval); - token = yylex(); - } - else - tableList[0] = new SQLResultSetTable(tableName, tableName); - - token = optWhereClause(token); // Where clause. - command = SQLElement.CMD_DELETE; - break; - - case TK_DROP: - switch (yylex()) - { - case TK_TABLE: // Drop table. - if (yylex() == TK_IDENT) - tableList[0] = new SQLResultSetTable(yylval); // There's no alias table name here. - else - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - command = SQLElement.CMD_DROP_TABLE; - break; - - case TK_INDEX: // Drop index. - if ((token = colnameCommaList()) != TK_ON || yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableList[0] = new SQLResultSetTable(yylval); // There's no alias table name here. - command = SQLElement.CMD_DROP_INDEX; - break; - - default: - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - - token = yylex(); - break; - - case TK_INSERT: // Insert. - if (yylex() != TK_INTO || yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableList[0] = new SQLResultSetTable(yylval); // There's no alias table name here. - - if ((token = yylex()) == TK_OPEN) // Reads the field list. - { - if (colnameCommaList() != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - token = yylex(); - } - - if (token != TK_VALUES || yylex() != TK_OPEN || listValues() != TK_CLOSE) // Reads the value list. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - // If the default order is not used, the number of values must be equal to the number of fields. - if (fieldNamesSize != 0 && fieldNamesSize != fieldValuesSize) - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH) - + '(' + fieldNamesSize + " != " + fieldValuesSize + ')'); - - command = SQLElement.CMD_INSERT; - token = yylex(); - break; - - case TK_SELECT: // Select. - if ((token = yylex()) == TK_DISTINCT) - token = yylex(); - if (fieldExp(token) != TK_FROM) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - token = optWhereClause(tableList()); // Table list and where clause. - - // order by and group by. - if (token == TK_GROUP) - { - if (yylex() != TK_BY) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - token = groupByClause(); - } - if (token == TK_ORDER) - { - if (yylex() != TK_BY) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - token = orderByClause(); - } - - SQLSelectClause selectAux = select; - - command = SQLElement.CMD_SELECT; - selectAux.tableList = new SQLResultSetTable[tableListSize]; - Vm.arrayCopy(tableList, 0, selectAux.tableList, 0, tableListSize); - - // Checks if the first field is the wild card. If so, assigns null to list, to indicate that all fields must be included. - if (selectAux.fieldList[0].isWildcard) - { - selectAux.fieldList = null; - selectAux.fieldsCount = 0; - } - else - { - // Compacts the resulting field list. - SQLResultSetField[] compactFieldList = new SQLResultSetField[selectAux.fieldsCount]; - Vm.arrayCopy(selectAux.fieldList, 0, compactFieldList, 0, selectAux.fieldsCount); - selectAux.fieldList = compactFieldList; - } - - break; - - case TK_UPDATE: // Update. - String tableAlias = null; - - // Table name. - if (yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - tableAlias = tableName = yylval; - - if ((token = yylex()) == TK_IDENT) // Alias table name. - { - tableAlias = yylval; - token = yylex(); - } - - if (token != TK_SET) // set key word. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - token = optWhereClause(updateExpCommalist()); // Update expression list and where clause. - - if (secondFieldUpdateTableName != null) // Verifies if there was an error on field.tableName. - { - if (!tableAlias.equals(firstFieldUpdateTableName)) - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + firstFieldUpdateAlias); - else - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + secondFieldUpdateAlias); - } - else if (firstFieldUpdateTableName != null && !tableAlias.equals(firstFieldUpdateTableName)) - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + firstFieldUpdateAlias); - - command = SQLElement.CMD_UPDATE; - tableList[0] = new SQLResultSetTable(tableName, tableAlias); - break; - - default: - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - if (token != LitebaseLex.YYEOF) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - - /** - * Deals with a list of identifiers separated by commas. - * - * @return The token after the list of identifiers. - */ - private int colnameCommaList() - { - int token, - fieldNamesSizeAux = fieldNamesSize; - String[] fieldNamesAux = fieldNames; - - do - { - if ((token = yylex()) == TK_ASTERISK) // This is necessary for dropping all indices. - { - fieldNamesAux[fieldNamesSizeAux++] = "*"; - return yylex(); - } - - if (token != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - fieldNamesAux[fieldNamesSizeAux++] = yylval; // Adds the column name. - } - while ((token = yylex()) == TK_COMMA); - fieldNamesSize = fieldNamesSizeAux; - return token; - } - - /** - * Deals with a list of rows of a table being created. - * - * @return The token after the list of rows. - * @throws InvalidNumberException If an internal method throws it. - */ - private int createColumnCommalist() throws InvalidNumberException - { - int token; - - while ((token = createColumn()) == TK_COMMA); - if (fieldListSize == 0) // The number of columns can't be zero. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - return token; - } - - /** - * Deals with a column declaration. - * - * @return The token after a column declaration. - * @throws InvalidNumberException If an internal method throws it. - */ - private int createColumn() throws InvalidNumberException - { - int token, - type, - size = 0; - boolean isPrimaryKey = false, - isNotNull = false; - String columnName, - strDefault = null; - - if ((token = yylex()) == TK_PRIMARY) // The next token after ',' is a primary key declaration. This is not treated here. - return token; - - // Column name. - if (token != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - columnName = yylval; - - // Column type. - if ((type = yylex()) == SQLElement.BOOLEAN || type > SQLElement.BLOB || type < SQLElement.CHARS) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - if (type == TK_VARCHAR) - type = SQLElement.CHARS; - - if (type == SQLElement.CHARS || type == SQLElement.BLOB) // Size and multiplier. - { - if (yylex() == TK_OPEN && yylex() == TK_NUMBER) - { - if ((size = Convert.toInt(yylval)) <= 0) - yyerror(LitebaseMessage.ERR_FIELD_SIZE_IS_NOT_INT); - - // juliana@253_15: now an exception is thrown if the size of a CHAR or VARCHAR is greater than 65535. - if (type == SQLElement.CHARS && size > (Convert.MAX_SHORT_VALUE << 1) + 1) - yyerror(LitebaseMessage.ERR_CHAR_TOO_BIG); - } - else - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - if (type == SQLElement.CHARS && yylex() != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - else if (type == SQLElement.BLOB) - { - if ((token = yylex()) != TK_IDENT && token != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - if (token == TK_IDENT) - { - if (yylval.equals("k")) // kilobytes. - size <<= 10; - else if (yylval.equals("m")) // megabytes. - size <<= 20; - else - yyerror(LitebaseMessage.ERR_INVALID_MULTIPLIER); - if (yylex() != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - } - if (size > (10 << 20)) // There is a size limit for a blob! - yyerror(LitebaseMessage.ERR_BLOB_TOO_BIG); - } - } - - if ((token = yylex()) == TK_NOCASE) // No case. - { - if (type == SQLElement.CHARS) - type = SQLElement.CHARS_NOCASE; - else - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - token = yylex(); - } - - if (token == TK_PRIMARY) // Simple primary key. - { - if (yylex() != TK_KEY) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - if (number_pk++ == 1) - yyerror(LitebaseMessage.ERR_PRIMARY_KEY_ALREADY_DEFINED); - if (type == SQLElement.BLOB) - yyerror(LitebaseMessage.ERR_BLOB_PRIMARY_KEY); - token = yylex(); - isPrimaryKey = true; - } - - if (token == TK_DEFAULT) // Default value. - { - if ((token = yylex()) == TK_NUMBER || token == TK_STR) - strDefault = yylval; - else if (token != TK_NULL) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - if (type == SQLElement.BLOB) // A blob can't have a default value. - yyerror(LitebaseMessage.ERR_BLOB_STRING); - - // A numeric type must have a number as a default value. A string, date or datetime type must have a string as a default value. - if (((type == SQLElement.CHARS || type == SQLElement.CHARS_NOCASE || type == SQLElement.DATE || type == SQLElement.DATETIME) && token == TK_NUMBER) - || ((type > SQLElement.CHARS && type < SQLElement.CHARS_NOCASE) && token == TK_STR)) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - token = yylex(); - } - - if (token == TK_NOT) // Not null. - { - if (yylex() != TK_NULL) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - token = yylex(); - isNotNull = true; - } - - fieldList[fieldListSize++] = new SQLFieldDefinition(columnName, type, size, isPrimaryKey, strDefault, isNotNull); - return token; - } - - /** - * Deals with an expression of an expression tree of a where clause. - * - * @param token The first token of the expression. - * @return The token after the expression. - */ - private int expression(int token) - { - if ((token = term(token)) == TK_OR) // expression = term or expression | term - { - // juliana@213_1: changed the way a tree with ORs is built in order to speed up queries with indices. - SQLBooleanClauseTree tree = setOperandType(SQLElement.OP_BOOLEAN_OR); - - (tree.rightTree = auxTree).parent = tree; - token = expression(yylex()); - (tree.leftTree = auxTree).parent = tree; - auxTree = tree; - } - - return token; - } - - /** - * Deals with a term of an expression tree of a where clause. - * - * @param token The first token of the term. - * @return The token after the term. - */ - private int term(int token) - { - if ((token = factor(token)) == TK_AND) // term = factor or factor | term - { - SQLBooleanClauseTree tree = setOperandType(SQLElement.OP_BOOLEAN_AND); - (tree.rightTree = auxTree).parent = tree; - token = term(yylex()); - (tree.leftTree = auxTree).parent = tree; - auxTree = tree; - } - - return token; - } - - /** - * Deals with a factor of an expression tree of a where clause. - * - * @param token The first token of the factor. - * @return The token after the factor. - */ - private int factor(int token) - { - SQLBooleanClauseTree tree = null; - - if (token == TK_OPEN) // factor = (expression) - { - if ((token = expression(yylex())) != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - return yylex(); - } - - if (token == TK_NOT) - { - if ((token = yylex()) == TK_OPEN) // factor = not (expression) - { - if ((token = expression(yylex())) != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - token = yylex(); - } - else // fator = not factor - token = factor(token); - - // The parent node will be the negation operator and the expression will be the right tree. - tree = setOperandType(SQLElement.OP_BOOLEAN_NOT); - (tree.rightTree = auxTree).parent = tree; - auxTree = tree; - - return token; - } - - // factor = single expression (< | > | = | <> | != | <= | >=) single expression - if ((token = singleExp(token)) == TK_EQUAL || token == TK_LESS || token == TK_DIFF || token == TK_GREATER || token == TK_GREATER_EQUAL - || token == TK_LESS_EQUAL) - { - tree = setOperandType(token); - (tree.leftTree = auxTree).parent = tree; - token = singleExp(yylex()); - (tree.rightTree = auxTree).parent = tree; - auxTree = tree; - return token; - } - - if (token == TK_IS) // factor = single expression is [not] null. - { - if ((token = yylex()) == TK_NOT) - { - tree = setOperandType(SQLElement.OP_PAT_IS_NOT); - token = yylex(); - } - else - tree = setOperandType(SQLElement.OP_PAT_IS); - if (token != TK_NULL) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - (tree.rightTree = setOperandType(SQLElement.OP_PAT_NULL)).parent = (tree.leftTree = auxTree).parent = tree; - auxTree = tree; - - return yylex(); - } - - if (token == TK_NOT) // factor = single expression not like [string | ?] - { - token = yylex(); - tree = setOperandType(SQLElement.OP_PAT_MATCH_NOT_LIKE); - } - else // factor = single expression like [string | ?] - tree = setOperandType(SQLElement.OP_PAT_MATCH_LIKE); - - SQLBooleanClause whereClause = getInstanceBooleanClause(); - SQLBooleanClauseTree rightTree = new SQLBooleanClauseTree(whereClause); - - if (token == TK_LIKE) - { - if ((token = yylex()) == TK_STR) // string - rightTree.setOperandStringLiteral(yylval); - else if (token == TK_INTERROGATION) // ? - { - rightTree.isParameter = true; - whereClause.paramList[whereClause.paramCount++] = rightTree; - } - else - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - (tree.rightTree = rightTree).parent = (tree.leftTree = auxTree).parent = tree; - auxTree = tree; - - return yylex(); - } - - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - return -1; - } - - /** - * Deals with a single expression of an expression tree of a where clause. - * - * @param token The first token of the single expression. - * @return The token after the single expression. - */ - private int singleExp(int token) - { - int auxToken; - SQLBooleanClauseTree tree; - - if (token == TK_NUMBER) // single expression = number - { - if ((tree = auxTree = new SQLBooleanClauseTree(getInstanceBooleanClause())).operandValue == null) - tree.operandValue = new SQLValue(); - - tree.operandValue.asString = yylval; // juliana@226a_20 - return yylex(); - } - else if (token == TK_STR) // single expression = string - { - (auxTree = new SQLBooleanClauseTree(getInstanceBooleanClause())).setOperandStringLiteral(yylval); - return yylex(); - } - else if (token == TK_INTERROGATION) // single expression = ? - { - SQLBooleanClause whereClause = getInstanceBooleanClause(); - - (whereClause.paramList[whereClause.paramCount++] = auxTree = new SQLBooleanClauseTree(whereClause)).isParameter = true; - return yylex(); - } - else if ((auxToken = dataFunction(token)) != -1) // single expression = function(...) - { - SQLBooleanClause booleanClause = getInstanceBooleanClause(); - int i = 1, - index = booleanClause.fieldsCount; - SQLResultSetField field = auxField; - - (auxTree = tree = new SQLBooleanClauseTree(booleanClause)).operandType = SQLElement.OP_IDENTIFIER; - int hashCode = tree.nameSqlFunctionHashCode = tree.nameHashCode = (tree.operandName = field.tableColName).hashCode(); - - // generates different indexes to repeted columns on where clause. - // Ex: where year(birth) = 2000 and day(birth) = 3. - while (booleanClause.fieldName2Index.exists(tree.nameSqlFunctionHashCode)) - tree.nameSqlFunctionHashCode = (hashCode << 5) - hashCode + i++ - 48; - - // Puts the hash code of the function name in the hash table. - booleanClause.fieldName2Index.put(tree.nameSqlFunctionHashCode, index); - - SQLResultSetField paramField = field.parameter = new SQLResultSetField(); // Creates the parameter field. - - // Sets the field and function parameter fields. - paramField.alias = paramField.tableColName = field.alias = field.tableColName = tree.operandName; - paramField.aliasHashCode = paramField.tableColHashCode = field.tableColHashCode = field.aliasHashCode = tree.nameHashCode; - field.dataType = SQLElement.dataTypeFunctionsTypes[field.sqlFunction]; - field.isDataTypeFunction = field.isVirtual = true; - booleanClause.fieldList[booleanClause.fieldsCount++] = field; // Puts the field in the field list. - - return auxToken; - } - else if (token != TK_NULL) // single expression = pure field. - { - token = pureField(token); - - SQLBooleanClause booleanClause = getInstanceBooleanClause(); - int i = 1, - index = booleanClause.fieldsCount; - SQLResultSetField field = auxField; - - (auxTree = tree = new SQLBooleanClauseTree(booleanClause)).operandType = SQLElement.OP_IDENTIFIER; - int hashCode = field.tableColHashCode = tree.nameSqlFunctionHashCode = tree.nameHashCode = (tree.operandName = field.tableColName).hashCode(); - - // rnovais@570_108: Generates different index to repeted columns on where - // clause. Ex: where year(birth) = 2000 and birth = '2008/02/11'. - while (booleanClause.fieldName2Index.exists(tree.nameSqlFunctionHashCode)) - tree.nameSqlFunctionHashCode = (hashCode << 5) - hashCode + i++ - 48; - - // Puts the hash code of the function name in the hash table. - booleanClause.fieldName2Index.put(tree.nameSqlFunctionHashCode, index); - - field.aliasHashCode = field.alias.hashCode(); // Sets the hash code of the field alias. - booleanClause.fieldList[booleanClause.fieldsCount++] = field; // Puts the field in the field list. - - return token; - } - - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - return -1; - } - - /** - * Deals with a list of values of an insert. - * - * @return The token after the list of values. - */ - private int listValues() - { - int token, - size = 0; - String[] values = fieldValues; - - do - switch (token = yylex()) - { - case TK_NULL: // Null. - size++; - break; - case TK_INTERROGATION: // A variable for prepared statements. - values[size++] = "?"; - break; - case TK_NUMBER: // A number. - case TK_STR: // A string. - values[size++] = yylval; - break; - default: // The list of values is finished or an error occurred. - { - if (size == 0) // There must be a value to be inserted. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - return token; - } - } - while ((token = yylex()) == TK_COMMA); - fieldValuesSize = size; - return token; - } - - /** - * Deals with a table list of a select. - * - * @return The token after the list of tables. - */ - private int tableList() - { - int token, - size = 0; - String tableName, - tableAlias; - IntHashtable tables = new IntHashtable(4); - SQLResultSetTable[] list = tableList; - - do - { - if ((token = yylex()) != TK_IDENT) // Not a table name, return. - { - if ((tableListSize = size) == 0) // There must be at least a table. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - return token; - } - tableName = tableAlias = yylval; // Table name. - - // Table alias. - if ((token = yylex()) == TK_AS) - token = yylex(); - if (token == TK_IDENT) - { - tableAlias = yylval; - token = yylex(); - } - - // The table name alias must be unique. - int hash = tableAlias.hashCode(); - if (tables.exists(hash)) - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_NOT_UNIQUE_ALIAS_TABLE) + tableAlias); - else - tables.put(hash, tables.size()); - - list[size++] = new SQLResultSetTable(tableName, tableAlias); - } - while (token == TK_COMMA); - tableListSize = size; - return token; - } - - /** - * Deals with a list of expressions of a select. - * - * @param token A token to be used by the list of expressions. - * @return The token after the list of expressions. - */ - private int fieldExp(int token) - { - SQLSelectClause selectAux = select; - SQLResultSetField[] resultFieldList = selectAux.fieldList; - - if (token == TK_ASTERISK) // All fields. - { - // Adds a wildcard field. - (resultFieldList[selectAux.fieldsCount++] = new SQLResultSetField()).isWildcard = true; - token = yylex(); - } - else - { - String alias = null; - - do - { - if (token == TK_COMMA) // Gets the next field list token. - token = yylex(); - - if ((token = field(token)) == TK_AS) // There is an alias. - { - if (yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - alias = yylval; - token = yylex(); - } - else - { - // If the alias_name is null, the alias must be the name of the column. This was already done before. - // If the alias is null and the field is a virtual column, raises an exception, since virtual columns require explicit aliases. - if (auxField.isVirtual) - yyerror(LitebaseMessage.ERR_REQUIRED_ALIAS); - - alias = auxField.alias; // The null alias name is filled as tableColName or tableName.tableColName, which was set before. - } - - // Checks if the alias has not already been used by a predecessor. - int i = selectAux.fieldsCount - 1; - - while (--i >= 0) - if (resultFieldList[i].alias.equals(alias)) - yyerrorWithMessage(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATE_ALIAS) + alias); - - auxField.aliasHashCode = (auxField.alias = alias).hashCode(); // Assigns the alias. - } - while (token == TK_COMMA); - } - return token; - } - - /** - * Deals with a list of update expressions. - * - * @return The token after the list of update expressions. - */ - private int updateExpCommalist() - { - int token, - size = 0; - String[] values = fieldValues; - String[] names = fieldNames; - SQLResultSetField field; - - do - { - if (pureField(yylex()) != TK_EQUAL) // field being updated. - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - field = auxField; - - // New value. - if ((token = yylex()) == TK_STR || token == TK_NUMBER) // A string or a number. - values[size++] = yylval; - else if (token == TK_NULL) // null - size++; - else if (token == TK_INTERROGATION) // A prepared statement parameter. - values[size++] = "?"; - else - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - if (firstFieldUpdateTableName == null) // After the table name verification, the associated table name on the field name is discarded. - { - if (field.tableName != null) - { - firstFieldUpdateTableName = field.tableName; - firstFieldUpdateAlias = field.alias; - } - } - else if (!field.tableName.equals(firstFieldUpdateTableName)) - - // Verifies if it is different. - // There is an error: update has just one table. This error will raise an exception later on. - { - secondFieldUpdateTableName = field.tableName; - secondFieldUpdateAlias = field.alias; - } - names[size - 1] = field.tableColName; - } - while ((token = yylex()) == TK_COMMA); - if ((fieldNamesSize = fieldValuesSize = size) == 0) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - return token; - } - - /** - * Deals with a field. - * - * @param token A token to be used by the field. - * @return The token after the field. - */ - private int field(int token) - { - int tokenAux; - SQLSelectClause selectAux = select; - SQLResultSetField field = null; - - if (token == TK_IDENT) // A pure field. - { - token = pureField(token); - selectAux.fieldList[selectAux.fieldsCount++] = field = auxField; - field.tableColHashCode = field.tableColName.hashCode(); - selectAux.hasRealColumns = true; - tokenAux = token; - } - else - { - if ((tokenAux = dataFunction(token)) != -1) // A function applied to a field. - { - // Sets the field. - field = auxField; - field.isDataTypeFunction = field.isVirtual = true; - field.dataType = SQLElement.dataTypeFunctionsTypes[field.sqlFunction]; - - // Sets the function parameter. - SQLResultSetField paramField = field.parameter = new SQLResultSetField(); - paramField.alias = paramField.tableColName = field.tableColName; - paramField.tableColHashCode = paramField.tableColName.hashCode(); - field.tableColHashCode = paramField.aliasHashCode = paramField.tableColHashCode; - } - else if ((tokenAux = aggFunction(token)) != -1) // An aggregation function applied to a field. - { - // Sets the field. - field = auxField; - field.isAggregatedFunction = field.isVirtual = true; - field.dataType = SQLElement.aggregateFunctionsTypes[field.sqlFunction]; - - // Sets the parameter, if there is such one. - if (field.sqlFunction != SQLElement.FUNCTION_AGG_COUNT) - { - // Sets the function parameter. - SQLResultSetField paramField = field.parameter = new SQLResultSetField(); - paramField.alias = paramField.tableColName = field.tableColName; - paramField.tableColHashCode = paramField.tableColName.hashCode(); - field.tableColHashCode = paramField.aliasHashCode = paramField.tableColHashCode; - } - - selectAux.hasAggFunctions = true; - } - else - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - - selectAux.fieldList[select.fieldsCount++] = field; // Sets the select statement. - } - - return tokenAux; - } - - /** - * Deals with a pure field. - * - * @param token A token to be used by the pure field. - * @return The token after the pure field. - */ - private int pureField(int token) - { - SQLResultSetField field = auxField = new SQLResultSetField(); - - if ((token = yylex()) == TK_DOT) // table.fieldName - { - field.tableName = field.alias = yylval; - if (yylex() != TK_IDENT) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - field.alias += '.' + (field.tableColName = yylval); - token = yylex(); - } - else // A simple field. - field.tableColName = field.alias = yylval; - - return token; - } - - /** - * Deals with a data function. - * - * @param token A token witch is possibly a data function token. - * @return The next token or -1 if it is not a data function. - */ - private int dataFunction(int token) - { - int function; - - switch (token) - { - case TK_ABS: // Abs function. - function = SQLElement.FUNCTION_DT_ABS; - break; - case TK_DAY: // Day function. - function = SQLElement.FUNCTION_DT_DAY; - break; - case TK_HOUR: // Hour function. - function = SQLElement.FUNCTION_DT_HOUR; - break; - case TK_LOWER: // Lower function. - function = SQLElement.FUNCTION_DT_LOWER; - break; - case TK_MILLIS: // Millis function. - function = SQLElement.FUNCTION_DT_MILLIS; - break; - case TK_MINUTE: // Minute function. - function = SQLElement.FUNCTION_DT_MINUTE; - break; - case TK_MONTH: // Month function. - function = SQLElement.FUNCTION_DT_MONTH; - break; - case TK_SECOND: // Second function. - function = SQLElement.FUNCTION_DT_SECOND; - break; - case TK_UPPER: // Upper function. - function = SQLElement.FUNCTION_DT_UPPER; - break; - case TK_YEAR: // Year function. - function = SQLElement.FUNCTION_DT_YEAR; - break; - default: - return -1; - } - if (yylex() != TK_OPEN || pureField(yylex()) != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - auxField.sqlFunction = function; - return yylex(); - } - - /** - * Deals with a aggregation function. - * - * @param token A token witch is possibly a data function token. - * @return The next token or -1 if it is not a data function. - */ - private int aggFunction(int token) - { - int function; - - switch (token) - { - case TK_AVG: - function = SQLElement.FUNCTION_AGG_AVG; - break; - case TK_COUNT: - function = SQLElement.FUNCTION_AGG_COUNT; - break; - case TK_MAX: - function = SQLElement.FUNCTION_AGG_MAX; - break; - case TK_MIN: - function = SQLElement.FUNCTION_AGG_MIN; - break; - case TK_SUM: - function = SQLElement.FUNCTION_AGG_SUM; - break; - default: - return -1; - } - if (token == TK_COUNT) - { - if (yylex() != TK_OPEN || yylex() != TK_ASTERISK || yylex() != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - auxField = new SQLResultSetField(); - } - else if (yylex() != TK_OPEN || pureField(yylex()) != TK_CLOSE) - yyerror(LitebaseMessage.ERR_SYNTAX_ERROR); - auxField.sqlFunction = function; - return yylex(); - } - - /** - * Deals with a possible where clause. - * - * @param token The token where if it is a where clause. - * @return The token received if it is not a where clause or the token after the where clause. - */ - private int optWhereClause(int token) - { - if (token == TK_WHERE) // Where clause. - { - token = expression(yylex()); - - // Compacts the field list of the where clause. - SQLBooleanClause clause = whereClause; - SQLResultSetField[] compactFieldList = new SQLResultSetField[clause.fieldsCount]; - Vm.arrayCopy(clause.fieldList, 0, compactFieldList, 0, clause.fieldsCount); - clause.fieldList = compactFieldList; - clause.expressionTree = auxTree; - } - return token; - } - - /** - * Deals with an order by clause. - * - * @return The first token after the order by clause. - */ - private int orderByClause() - { - int token; - boolean direction; - SQLSelectClause selectAux = select; - - do - { - direction = true; - - // Ascending or descending order. - if ((token = field(yylex())) == TK_ASC) - token = yylex(); - else if (token == TK_DESC) - { - direction = false; - token = yylex(); - } - - selectAux.fieldsCount--; - addColumnFieldOrderGroupBy(auxField, direction, true); - } - while (token == TK_COMMA); - - // Compacts the order by field list. - SQLColumnListClause orderByAux = orderBy; - SQLResultSetField[] compactFieldList = new SQLResultSetField[orderByAux.fieldsCount]; - Vm.arrayCopy(orderByAux.fieldList, 0, compactFieldList, 0, orderByAux.fieldsCount); - orderByAux.fieldList = compactFieldList; - - return token; - } - - /** - * Deals with a group by clause. - * - * @return The first token after the group by clause. - */ - private int groupByClause() - { - int token; - SQLSelectClause selectAux = select; - - do - { - token = field(yylex()); - selectAux.fieldsCount--; - addColumnFieldOrderGroupBy(auxField, true, false); - } - while (token == TK_COMMA); - - // Compacts the group by field list. - SQLColumnListClause groupByAux = groupBy; - SQLResultSetField[] compactFieldList = new SQLResultSetField[groupByAux.fieldsCount]; - Vm.arrayCopy(groupByAux.fieldList, 0, compactFieldList, 0, groupByAux.fieldsCount); - groupByAux.fieldList = compactFieldList; - - if (token == TK_HAVING) // Adds the expression tree of the where clause. - { - isWhereClause = false; // Indicates if the clause is a where or a having clause. - token = expression(yylex()); - havingClause.expressionTree = auxTree; - - // Compacts the having clause field list. - SQLBooleanClause clause = havingClause; - compactFieldList = new SQLResultSetField[clause.fieldsCount]; - Vm.arrayCopy(clause.fieldList, 0, compactFieldList, 0, clause.fieldsCount); - clause.fieldList = compactFieldList; - } - - return token; - } - - /** - * Sets the operand type. - * - * @param The operand type. - * @return A boolean clause tree with this operand type. - */ - private SQLBooleanClauseTree setOperandType(int operandType) - { - SQLBooleanClauseTree tree = new SQLBooleanClauseTree(getInstanceBooleanClause()); - tree.operandType = operandType; - return tree; - } - - /** - * Adds a column field to the order field list. - * - * @param field The field to be added. - * @param isAscending Indicates the ordering used. - * @param isOrder by true if the field comes from an order be clause. false, otherwise. - */ - private void addColumnFieldOrderGroupBy(SQLResultSetField field, boolean isAscending, boolean isOrderBy) - { - SQLColumnListClause listClause = isOrderBy? getInstanceColumnListClauseOrderBy() - : getInstanceColumnListClauseGroupBy(); - - field.tableColHashCode = field.aliasHashCode = field.tableColName.hashCode(); - field.isAscending = isAscending; - listClause.fieldList[listClause.fieldsCount++] = field; - } - - /** - * Error message with code. - * - * @param error The error code. - * @throws SQLParseException - */ - void yyerror(int error) throws SQLParseException - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_MESSAGE_START) - + LitebaseMessage.getMessage(error) + LitebaseMessage.getMessage(LitebaseMessage.ERR_MESSAGE_POSITION) + lexer.yyposition + '.'); - } - - /** - * Error message with a error message. - * - * @param message The error message. - * @throws SQLParseException - */ - private void yyerrorWithMessage(String message) - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_MESSAGE_START) + message + LitebaseMessage.getMessage(LitebaseMessage.ERR_MESSAGE_POSITION) + lexer.yyposition + '.'); - } -} diff --git a/LitebaseSDK/src/java/litebase/MarkBits.java b/LitebaseSDK/src/java/litebase/MarkBits.java deleted file mode 100644 index 6aa78c2fb2..0000000000 --- a/LitebaseSDK/src/java/litebase/MarkBits.java +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.IOException; -import totalcross.util.IntVector; - -/** - * Generates the result set indexed rows map from the associated table indexes applied to the associated WHERE clause. This class should only be used - * if the result set has a WHERE clause. - */ -@Deprecated -class MarkBits -{ - /** - * The index bitmap of the where clause. - */ - IntVector indexBitmap; - - /** - * The value of a bit of the bitmap. - */ - boolean bitValue; - - /** - * Indicates if a value is equal or not. - */ - private boolean isNoLongerEqual; - - /** - * The left operator. - */ - byte[] leftOp; - - /** - * The right operator. - */ - byte[] rightOp; - - /** - * The left key. - */ - Key leftKey; - - /** - * The right key. - */ - Key rightKey; - - /** - * Resets the object and the bitmap. - * - * @param idx The index whose bitmap is being reseted. - * @param bits The number of the bitmap. - */ - void reset(Index idx, int bits) - { - int size = idx.types.length; - - leftOp = new byte[size]; - rightOp = new byte[size]; - leftKey = new Key(idx); - isNoLongerEqual = false; - bitValue = true; - rightKey = null; - indexBitmap = new IntVector(1); - indexBitmap.ensureBit(bits); - } - - // juliana@253_5: removed .idr files from all indices and changed its format. - /** - * Climbs on a key. - * - * @param key The key to be climbed on. - * @return false if the key could not be climbed; true, otherwise. - * @throws IOException If an internal method throws it. - */ - boolean onKey(Key k) throws IOException - { - int r0 = rightOp[0]; - int l0 = leftOp[0]; - - // juliana@230_20: solved a possible crash when using aggregation functions with strings. - PlainDB db = k.index.table.db; - SQLValue key = k.keys[0]; - int type = leftKey.index.types[0]; - - if (key.asString == null && (type == SQLElement.CHARS || type == SQLElement.CHARS_NOCASE)) // A string may not be loaded. - { - db.dbo.setPos(key.asInt); // Gets and sets the string position in the .dbo. - key.asString = db.loadString(); - } - - if (rightKey != null) - { - int comp = Utils.arrayValueCompareTo(k.keys, rightKey.keys, k.index.types, null); // Compares the key with the right key. - - // If key <= right key, stops. - if (r0 == SQLElement.OP_REL_LESS_EQUAL && comp > 0) - return false; - - // if key < right key, stops. - if (r0 == SQLElement.OP_REL_LESS && comp >= 0) - return false; - } - - // For inclusion operations, just uses the value. - if (l0 == SQLElement.OP_REL_EQUAL || l0 == SQLElement.OP_REL_GREATER_EQUAL || (l0 == SQLElement.OP_REL_GREATER && isNoLongerEqual)) - onValue(k.record); // Climbs on the value. - else if (l0 == SQLElement.OP_REL_GREATER) // The key can still be equal. - { - if (Utils.arrayValueCompareTo(leftKey.keys, k.keys, leftKey.index.types, null) != 0) // Compares the key with the left key. - { - isNoLongerEqual = true; - onValue(k.record); // Climbs on the value. - } - } - else // OP_PAT_MATCH_LIKE - { - String val = key.asString; - if (type == SQLElement.CHARS_NOCASE) - val = val.toLowerCase(); - - // juliana@230_3: corrected a bug of LIKE using DATE and DATETIME not returning the correct result. - else - val = Utils.formatDateDateTime(db.driver.sBuffer, type, key); - - if (val.startsWith(leftKey.keys[0].asString)) // Only starts with are used with indices. - onValue(k.record); // Climbs on the value. - else - return false; // Stops the search. - - } - return true; // Does not visit this value, but continues the search. - } - - /** - * Climbs on a value. - * - * @param record The value record to be climbed on. - */ - void onValue(int record) - { - if (record != Key.NO_VALUE) - indexBitmap.setBit(record, bitValue); // (Un)sets the corresponding bit on the bit array. - } -} diff --git a/LitebaseSDK/src/java/litebase/MemoryFile.java b/LitebaseSDK/src/java/litebase/MemoryFile.java deleted file mode 100644 index b84d9adc42..0000000000 --- a/LitebaseSDK/src/java/litebase/MemoryFile.java +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.Vm; - -/** - * This is a memory file, ie, a file that is allocated in memory and never dumped to disk. Used for result sets. - */ -@Deprecated -class MemoryFile extends XFile -{ - /** - * The memory file, a buffer array. - */ - private byte[] fbuf; - - /** - * Creates a memory file - */ - MemoryFile() - { - fbuf = new byte[0]; - } - - /** - * Sets the position in the buffer. - * - * @param newPos The new position in the buffer. - */ - void setPos(int newPos) - { - pos = newPos; - } - - /** - * Increases the buffer for the memory file. - * - * @param newSize the new size of the buffer. - */ - void growTo(int newSize) - { - byte[] temp = new byte[newSize]; - Vm.arrayCopy(fbuf, 0, temp, 0, size); // Copies the old buffer. - fbuf = temp; - size = newSize; - } - - // guich@201_9: always shrink the .db and .dbo memory files. - /** - * Shrinks the buffer for the memory file if there is unused space. - * - * @param newSize the new size of the buffer. - */ - void shrinkTo(int newSize) - { - byte[] temp = new byte[newSize]; - Vm.arrayCopy(fbuf, 0, temp, 0, size = newSize); // Copies the the old buffer. - fbuf = temp; - } - - /** - * Reads bytes from the buffers. - * - * @param buf The byte array to read data into. - * @param start The offset position in the array. - * @param count The number of bytes to read. - * @return The number of bytes read. - */ - public int readBytes(byte[] buf, int start, int count) - { - Vm.arrayCopy(fbuf, pos, buf, start, count); - pos += count; - return count; - } - - /** - * Writes bytes into the buffer. - * - * @param buf The byte array to write data from. - * @param start The offset position in the array. - * @param count The number of bytes to write. - * @return The number of bytes written. - */ - public int writeBytes(byte[] buf, int start, int count) - { - Vm.arrayCopy(buf, start, fbuf, pos, count); - pos += count; - return count; - } -} diff --git a/LitebaseSDK/src/java/litebase/Node.java b/LitebaseSDK/src/java/litebase/Node.java deleted file mode 100644 index 983a8eb303..0000000000 --- a/LitebaseSDK/src/java/litebase/Node.java +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -// juliana@253_5: removed .idr files from all indices and changed its format. -// juliana@253_6: the maximum number of keys of a index was duplicated. -/** - * This is the implementation of a B-Tree. It is used to store the table indices. It has some improvements for both memory usage, disk space, and - * speed, targeting the creation of indices, where the table's record is far greater than the index record. - */ -@Deprecated -class Node -{ - /** - * Indicates if a node is a leaf. - */ - static final int LEAF = 0xFFFF; - - /** - * The grow size of the node, which must be a power of 2. - */ - static final int NODEGROWSIZE = 64; - - /** - * The maximum number of nodes in an index. - */ - static final int MAX_IDX = 65534; // juliana@253_6: The maximum number of keys of a index was duplicated. - - /** - * The size of the node. - */ - int size; - - /** - * The index of a node in the B-Tree. - */ - int idx = -1; - - /** - * The keys that this node stores. - */ - Key[] keys; - - /** - * This children nodes. - */ - int [] children; - - /** - * The index of this node. - */ - Index index; - - /** - * Indicates if a node is dirty. - */ - boolean isDirty; - - /** - * Creates a new node for an index. - * - * @param anIndex The index of the node to be created. - */ - Node(Index anIndex) - { - // Creates this node keys. - Key[] keysAux = keys = new Key[anIndex.btreeMaxNodes]; - int i = anIndex.btreeMaxNodes; - while (--i >= 0) - keysAux[i] = new Key(index = anIndex); - - children = new int[anIndex.btreeMaxNodes + 1]; // Each array has one extra component, to allow for possible overflow. - } - - /** - * Loads a node. - * - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void load() throws IOException, InvalidDateException - { - int i = -1, - length; - Index indexAux = index; - XFile fnodes = indexAux.fnodes; - Key[] keysAux = keys; - int[] childrenAux = children; - - fnodes.setPos(idx * indexAux.nodeRecSize); - fnodes.readBytes(indexAux.basbuf, 0, indexAux.nodeRecSize); // Reads all the record at once. - - // Loads the keys. - DataStreamLB ds = indexAux.basds; // juliana@253_8: now Litebase supports weak cryptography. - indexAux.bas.reset(); - length = size = ds.readUnsignedShort(); - while (++i < length) - keysAux[i].load(ds); - - // Loads the node children. - i = -1; - while (++i <= length) - childrenAux[i] = ds.readUnsignedShort(); - - Convert.fill(childrenAux, i + 1, indexAux.btreeMaxNodes + 1, LEAF); // Fills the non-used indexes with TERMINAL. - } - - /** - * Saves a dirty key. - * - * @param currPos The current position in the file where the key should be saved. - * @throws IOException If an internal method throws it. - */ - void saveDirtyKey(int currPos) throws IOException - { - Index indexAux = index; - - // Positions the file pointer at the insert position. - indexAux.fnodes.setPos(idx * indexAux.nodeRecSize + 2 + indexAux.keyRecSize * currPos + (indexAux.keyRecSize - Key.VALREC_SIZE)); - - indexAux.bas.reset(); - indexAux.basds.writeInt(keys[currPos].record); - indexAux.fnodes.writeBytes(indexAux.basbuf, 0, 4); - } - - /** - * Saves a node. - * - * @param isNew Indicates if it is a new node, not saved yet. - * @param left The left child. - * @param right The right child. - * @return The position of this node. - * @throws DriverException If the index gets too large. - * @throws IOException If an internal method throws it. - */ - int save(boolean isNew, int left, int right) throws IOException - { - Index indexAux = index; - int i, - idxAux = idx, - recSize = indexAux.nodeRecSize; - NormalFile fnodes = indexAux.fnodes; - - if (isNew) - { - if ((idxAux = indexAux.nodeCount++) >= MAX_IDX) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INDEX_LARGE)); - - if (indexAux.isWriteDelayed) - { - if (idxAux * recSize == fnodes.size) // Grows more than 1 record per time. - fnodes.growTo((idxAux + NODEGROWSIZE) * recSize); - } - else - fnodes.growTo((idxAux + 1) * recSize); // Opens space for the node. - - } - fnodes.setPos(idxAux * recSize); // Rewinds to insert position. - - DataStreamLB ds = indexAux.basds; // juliana@253_8: now Litebase supports weak cryptography. - ByteArrayStream bas = indexAux.bas; - Key[] keysAux = keys; - int[] childrenAux = children; - - bas.reset(); - ds.writeShort(right - left); - - i = left - 1; - while (++i < right) // Saves the keys. - keysAux[i].save(ds); - - // Saves the children. - i = left - 1; - while (++i <= right) - ds.writeShort(childrenAux[i]); - - // juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - if (isNew && idxAux > 0 && idxAux <= indexAux.btreeMaxNodes) - { - Node[] firstLevel = indexAux.firstLevel; - Node node = firstLevel[idxAux - 1]; - - if (node == null) - node = firstLevel[idxAux - 1] = new Node(indexAux); - - Key[] keys = node.keys; - node.idx = idxAux; - Vm.arrayCopy(childrenAux, left, node.children, 0, (i = node.size = right - left) + 1); - while (--i >= 0) - { - keys[i].set(keysAux[i + left].keys); - keys[i].record = keysAux[i + left].record; - } - node.isDirty = false; - } - - ds.pad(bas.available()); // Fills the rest with zeros. - fnodes.writeBytes(indexAux.basbuf, 0, bas.getPos()); - isDirty = false; - return idxAux; - } - - /** - * Constructs a B-Tree node with at most k keys, initially with one element, item, and two children: left and right. - * - * @param item The key to be saved. - * @param left The left child. - * @param right The right child. - */ - void set(Key item, int left, int right) - { - Key[] keysAux = keys; - int[] childrenAux = children; - - size = 1; - keysAux[0].set(item.keys); - keysAux[0].record = item.record; - childrenAux[0] = left; - childrenAux[1] = right; - } - - /** - * Returns the index of the leftmost element of this node that is not less than item, using a binary search. - * - * @param item The key to be found. - * @param isInsert Indicates if the method is called by Index.insert() - * @return The position of the key. - * @throws IOException If an internal method throws it. - */ - int findIn(Key item, boolean isInsert) throws IOException - { - Index indexAux = index; - PlainDB plainDB = indexAux.table.db; - Key[] keysAux = keys; - SQLValue[] itemKeys = item.keys; - byte[] types = indexAux.types; - int r = size - 1, - l = (isInsert && indexAux.isOrdered && r > 0)? r : 0, // juliana@201_3: If the insertion is ordered, the position being seached is the last. - m, - comp; - - while (l <= r) - { - if ((comp = Utils.arrayValueCompareTo(itemKeys, keysAux[m = (l + r) >> 1].keys, types, plainDB)) == 0) - return m; - else - if (comp < 0) - r = m - 1; - else - l = m + 1; - } - return l; - } - - /** - * Inserts element item, with left and right children at the right position in this node. - * - * @param item The key to be saved. - * @param leftChild The left child of the node. - * @param rightChild The right child of the node. - * @param ins The position where to insert the key. - * @throws IOException If an internal method throws it. - */ - void insert(Key item, int leftChild, int rightChild, int ins) throws IOException - { - int sizeAux = size, - l = sizeAux - ins; - Key[] keysAux = keys; - int[] childrenAux = children; - - if (l > 0) - { - int i = sizeAux + 1; - while (--i > ins) - { - keysAux[i].set(keysAux[i - 1].keys); - keysAux[i].record = keysAux[i - 1].record; - } - Vm.arrayCopy(childrenAux, ins + 1, childrenAux, ins + 2, l); - } - keysAux[ins].set(item.keys); - keysAux[ins].record = item.record; - childrenAux[ins] = leftChild; - childrenAux[ins + 1] = rightChild; - sizeAux = ++size; - - if (index.isWriteDelayed) // Only saves the key if it is not to be saved later. - isDirty = true; - else - save(false, 0, sizeAux); - } - - /** - * Sets the flag that indicates if the not should have its write process delayed or not. - * - * @param delayed The new value of the flag. - * @throws IOException If an internal method throws it. - */ - void setWriteDelayed(boolean delayed) throws IOException - { - if (index.isWriteDelayed && isDirty && !delayed) // Before changing the flag, flushs the node. - save(false, 0, size); - } -} diff --git a/LitebaseSDK/src/java/litebase/NormalFile.java b/LitebaseSDK/src/java/litebase/NormalFile.java deleted file mode 100644 index 7cae750e0b..0000000000 --- a/LitebaseSDK/src/java/litebase/NormalFile.java +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; - -/** - * This is a normal file, ie, a file that is stored in disk. - */ -@Deprecated -class NormalFile extends XFile -{ - /** - * The cache size for the table files. - */ - static final int CACHE_INITIAL_SIZE = 2048; - - /** - * .db extension (database file). - */ - static final String DB_EXT = ".db"; - - /** - * .dbo extension (strings and blobs file). - */ - static final String DBO_EXT = ".dbo"; - - /** - * The file. - */ - File f; - - // Cache support. - /** - * The cache for the file. - */ - private byte[] cache; - - /** - * The initial position of the cache. - */ - private int cacheIni; - - /** - * The final position of the cache. - */ - private int cacheEnd; - - /** - * The initial size of the cache. - */ - private int cacheInitialSize; - - /** - * The initial position of the cache that is dirty. - */ - private int cacheDirtyIni; - - /** - * The final position of the cache that is dirty. - */ - private int cacheDirtyEnd; - - /** - * Indicates if the cache is dirty and must be saved. - */ - boolean cacheIsDirty; - - // juliana@227_3: improved table files flush dealing. - /** - * Indicates if the cache file should not be flushed. - */ - boolean dontFlush; - - // All methods just call the File ones. - /** - * Creates a disk file to store tables. - * - * @param name The name of the file. - * @param isCreation Indicates if the file must be created or just open. - * @param cacheSize The cache size to be used. -1 should be passed if the default value is to be used. - * @throws IOException If an internal method throws it. - */ - NormalFile(String name, boolean isCreation, int cacheSize) throws IOException - { - f = new File(name, isCreation? File.CREATE_EMPTY : File.READ_WRITE) // Opens or creates the file. - { - // flsobral@lb201_1: Overriding finalize method prevents files from being finalized before the LitebaseConnection. - protected synchronized void finalize() - { - if (LitebaseConnection.htDrivers.size() == 0) - super.finalize(); - } - }; - size = f.getSize(); // Gets its size. - f.setPos(0); // Its current position is the first one. - - if (cacheSize != -1) - cache = new byte[cacheInitialSize = cacheSize]; - } - - /** - * Reads file bytes. - * - * @param buf The byte array to read data into. - * @param start The offset position in the array. - * @param count The number of bytes to read. - * @return The number of bytes read. - * @throws IOException If an internal method throws it. - */ - public int readBytes(byte[] buf, int start, int count) throws IOException - { - if (cacheInitialSize < count || pos < cacheIni || (pos + count) > cacheEnd) - refreshCache(count); - System.arraycopy(cache, pos - cacheIni, buf, start, count); - pos += count; - return count; - } - - /** - * Write bytes in a file. - * - * @param buf The byte array to write data from. - * @param start The offset position in the array. - * @param count The number of bytes to write. - * @return count, to indicate that everything is ok. - * @throws IOException If an internal method throws it. - */ - public int writeBytes(byte[] buf, int start, int count) throws IOException - { - if (cacheInitialSize < count || pos < cacheIni || (pos + count) > cacheEnd) - refreshCache(count); - System.arraycopy(buf, start, cache, pos - cacheIni, count); - cacheIsDirty = true; - if (pos < cacheDirtyIni) - cacheDirtyIni = pos; - pos += count; - if (pos > cacheDirtyEnd) - cacheDirtyEnd = pos; - return count; - } - - /** - * Enlarges the file. This method MUST be called to grow the file - otherwise, getSize() won't work correctly. - * - * @param newSize The new size for the file. - * @throws IOException If an internal method throws it. - */ - void growTo(int newSize) throws IOException - { - f.setSize(newSize); // Enlarges the file and sets the new size. - - // juliana@227_23: solved possible crashes when using a table recovered which was being used with setRowInc(). - if (newSize - size > 0) // juliana@230_18: removed possible garbage in table files. - { - f.setPos(size); - f.writeBytes(new byte[newSize - size]); - } - pos = (size = newSize) - 1; // The current position is the last one. - } - - /** - * Sets the current file position. - * - * @param newPos The new file position. - * @throws IOException If an internal method throws it. - * @throws DriverException If the table is corrupted and its access tries to read/write after the file end. - */ - void setPos(int newPos) throws IOException, DriverException - { - if (newPos > size) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_CORRUPTED)); - if (pos != newPos) // Optimization: ignores if the position is unchanged. - f.setPos(pos = newPos); - } - - /** - * Flushs the cache into the disk. - * - * @throws IOException If an internal method throws it. - */ - void flushCache() throws IOException - { - f.setPos(cacheDirtyIni); - cacheIsDirty = false; - f.writeBytes(cache, cacheDirtyIni - cacheIni, cacheDirtyEnd - cacheDirtyIni); - } - - /** - * The cache must be refreshed if what is desired is not in it. - * - * @param count The number of bytes that must be read. - * @throws IOException If an internal method throws it or it is not possible to write all the data. - */ - private void refreshCache(int count) throws IOException, DriverException - { - if (cacheIsDirty) - flushCache(); - if (cacheInitialSize < count) - cache = new byte[cacheInitialSize = Math.max(CACHE_INITIAL_SIZE, count << 2)]; - if (size != 0) - { - f.setPos(cacheDirtyEnd = cacheIni = pos); - - // juliana@212_8: when reading a file, an exception must not be thrown when reading zero bytes. - if (f.readBytes(cache, 0, cacheInitialSize) == -1 && pos != size && count > 0) - throw new IOException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANT_READ)); - - // Uses the cache size even if less was read, otherwise when filling a table, the cache will have to be refreshed all the times. - cacheDirtyIni = cacheEnd = cacheIni + cacheInitialSize; - } - } - - /** - * Sets the cache size. Pass -1 to set it back to the default value. - * - * @param newSize The new cache size. - * @throws IOException If an internal method throws it. - */ - private void setCacheSize(int newSize) throws IOException - { - int newCacheSize = Math.max(CACHE_INITIAL_SIZE, newSize); - if (newCacheSize != cacheInitialSize) - { - if (cacheIsDirty) - flushCache(); - cacheEnd = -1; - cache = null; - cache = new byte[cacheInitialSize = newCacheSize]; - } - } - - /** - * Closes this file. - * - * @throws IOException If an internal method throws it. - */ - public void close() throws IOException - { - if (cacheIsDirty) - flushCache(); - if (finalPos > 0) // juliana@210a_11: fixed a problem that could crop data from database indices. - f.setSize(finalPos); // juliana@201_5: the .dbo file must be cropped so that it wont't be too large with zeros at the end of the file. - f.close(); - } - - /** - * If there's enough memory, loads this file into memory by increasing the cache size. - * - * @param turnOn true to use a very big cache size; false to use the default cahce size. - * @throws IOException If an internal method throws it. - */ - void loadIntoMemory(boolean turnOn) throws IOException - { - if (!turnOn) - setCacheSize(-1); - else - { - int cacheSize = size == 0? 8192 : size; // If nothing was loaded, starts with a good cache size. - if (cacheInitialSize < cacheSize && totalcross.sys.Vm.getFreeMemory() > cacheSize << 2) - setCacheSize(cacheSize); - } - } - - // juliana@253_19: corrected a possible table corruption after a purge or a rename table only on Java SE. - /** - * Renames a Litebase normal file. - * - * @param newName The new file name. - * @throws IOException If an internal method throws it. - */ - void rename(String newName) throws IOException - { - f.rename(newName); - f = new File(newName, File.READ_WRITE) // Opens or creates the file. - { - protected synchronized void finalize() - { - // flsobral@lb201_1: Overriding finalize method prevents files from being finalized before the LitebaseConnection. - if (LitebaseConnection.htDrivers.size() == 0) - super.finalize(); - } - }; - } -} diff --git a/LitebaseSDK/src/java/litebase/PlainDB.java b/LitebaseSDK/src/java/litebase/PlainDB.java deleted file mode 100644 index 2a65dc08b1..0000000000 --- a/LitebaseSDK/src/java/litebase/PlainDB.java +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.Convert; -import totalcross.util.*; - -/** - * This has the implementation of a database in a plain binary file. The data and the metadata (header) is written in one file (.db). The strings and - * the blobs are written in the .dbo file. The current number of records inside the database is discovered only when the database is open by getting - * its size and discounting the header size. This has a double advantage: it is not necessary to waste space storing the current record count, and it - * is not needed to save the record count at each insertion. - * - * This also has the implementation of a temporary database for ResultSet tables. - */ -@Deprecated -class PlainDB -{ - /** - * The minimum space for composed indices and primary key in the table header. - */ - private static final int COMP_IDX_PK_SIZE = 64; - - /** - * The database (.db) file. - */ - XFile db; - - /** - * The strings and blobs (.dbo) file. - */ - XFile dbo; - - /** - * The table header size. - */ - int headerSize = 512; - - /** - * The size of a row. - */ - int rowSize; - - /** - * The number of rows. - */ - int rowCount; - - /** - * The current row increment when inserting data on the table. - */ - int rowInc = Utils.DEFAULT_ROW_INC; - - /** - * The number of rows available. - */ - int rowAvail; - - /** - * An stream to write and read data from the table. - */ - ByteArrayStream bas; - - /** - * A buffer to read a row. - */ - byte[] basbuf; - - /** - * The data stream to read data from the table. - */ - DataStreamLB basds; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * The data stream to read from the .dbo. - */ - DataStreamLB dsdbo; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * The table name. - */ - String name; - - /** - * Indicates if the tables of this connection use ascii or unicode strings. - */ - boolean isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - - /** - * Indicates if the table uses cryptography. - */ - boolean useCrypto; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * Indicates whether a table used the wrong cryptography format. - */ - boolean useOldCrypto; - - /** - * The driver where this table file was created. - */ - LitebaseConnection driver; - - /** - * Creates a new PlainDB, loading or creating the table with the given name or creating a temporary table. - * - * @param name The name of the table. - * @param sourcePath The path where the table is to be open or created. - * @param create Defines if the file will be created if it doesn't exist. - * @throws IOException If an internal method throws it. - */ - PlainDB(String aName, String sourcePath, boolean create) throws IOException - { - // rnovais@101_1 - // Opens or creates the .db file. - db = openFile(aName == null? null : (name = aName) + NormalFile.DB_EXT, create, sourcePath); - - // Opens or creates the .dbo file. - try - { - dbo = openFile(aName == null? null : aName + NormalFile.DBO_EXT, create, sourcePath); - } - catch (IOException exception) // juliana@222_7: .db should be closed if .dbo cannot be openned on desktop and BlackBerry. - { - db.close(); - throw exception; - } - } - - /** - * Sets the size of a row. - * - * @param newRowSize The new row size. - * @param buffer A buffer. - */ - void setRowSize(int newRowSize, byte[] buffer) - { - rowSize = newRowSize; - - // juliana@253_8: now Litebase supports weak cryptography. - basds = new DataStreamLB(bas = new ByteArrayStream(basbuf = buffer), useCrypto); - dsdbo = new DataStreamLB(dbo, useCrypto); - - int size = db.size - headerSize; - if (size >= 0) - rowCount = size / rowSize; // Finds how many records are there. - } - - // rnovais@570_75 - /** - * Opens or creates a file, which can be a memory file or a disk file. - * - * @param name The file name. - * @param create Indicates if the file is to be created. - * @param sourcePath The file path. - * @return The file handle. - * @throws IOException If an internal method throws it. - */ - XFile openFile(String name, boolean create, String sourcePath) throws IOException - { - if (name == null) - return new MemoryFile(); - else - return new NormalFile(Utils.getFullFileName(name, sourcePath), create, -1); - } - - /** - * Adds a new record. The file pointer is positioned in the record's beginning so that the data can be written. Usually the record is first - * added, then the contents are written. - * - * @throws IOException If an internal method throws it. - */ - void add() throws IOException - { - if (--rowAvail <= 0) // Checks if there are no more space pre-allocated. - { - db.growTo((rowCount + rowInc) * rowSize + headerSize); - rowAvail = rowInc; - } - db.setPos(headerSize + rowCount * rowSize); // Sets the position to the start of the record. - bas.reset(); // Prepares the buffer to be written. - } - - /** - * Writes the data of the bas into the current file position. - * - * @throws IOException If an internal method throws it. - */ - void write() throws IOException - { - rowCount++; - db.writeBytes(basbuf, 0, rowSize); - } - - /** - * Reads a row at the given position into bas. - * - * @param pos The .db file record to be read. - * @throws IOException If an internal method throws it. - */ - void read(int pos) throws IOException - { - db.setPos(headerSize + pos * rowSize); - bas.reset(); - db.readBytes(basbuf, 0, rowSize); - } - - /** - * Rewrites a row at the given position. - * - * @param pos The .db file record to be read. - * @throws IOException If an internal method throws it. - */ - void rewrite(int pos) throws IOException - { - db.setPos(headerSize + pos * rowSize); - db.writeBytes(basbuf, 0, rowSize); - } - - // juliana@253_19: corrected a possible table corruption after a purge or a rename table only on Java SE. - /** - * Renames the files to the new given name. - * - * @param newName The new table name. - * @param sourcePath The files path. - * @throws IOException If an internal method throws it. - */ - void rename(String newName, String sourcePath) throws IOException - { - String newFullName = Utils.getFullFileName(newName, sourcePath) + NormalFile.DB_EXT; - - ((NormalFile)db).rename(newFullName); // rnovais@570_75 // Renames the .db file. - try - { - ((NormalFile)dbo).rename(newFullName + 'o'); // Renames the .dbo file. // rnovais@570_75 - } - catch (IOException exception) // Unlikely to occur - { - // If the file could not be renamed, the .db file should be renamed back. - newFullName = Utils.getFullFileName(name, sourcePath) + NormalFile.DB_EXT; - ((NormalFile)db).rename(newFullName); - throw exception; - } - - name = newName; - } - - /** - * Writes the given metadata to the header of the .db file. - * - * @param buf The data to be written. - * @param len The data length. - * @throws IOException If an internal method throws it. - */ - void writeMetaData(byte[] buf, int len) throws IOException - { - XFile dbFile = db; - - if (dbFile.size == 0) // The metadata size must have a free space for future composed indices or composed primary key. - { - int size = headerSize; - - // juliana@230_7: corrected a possible exception or crash when the table has too many columns and composed indices or PKs. - while (len > size || size - len < COMP_IDX_PK_SIZE) - size <<= 1; - dbFile.growTo(headerSize = size); - - // juliana@223_15: solved a bug that could corrupt tables created with a very large metadata size. - // juliana@253_8: now Litebase supports weak cryptography. - dbFile.setPos(4); - buf[4] = (byte)(useCrypto? size ^ 0xAA : size); - buf[5] = (byte)(useCrypto? (size >> 8) ^ 0xAA : (size >> 8)); - } - - dbFile.setPos(0); - dbFile.writeBytes(buf, 0, len); - } - - /** - * Reads the user metadata from the .db file header. - * - * @return The metadata. - * @throws IOException If an internal method throws it. - */ - byte[] readMetaData() throws IOException - { - byte[] buf = new byte[headerSize]; - db.setPos(0); - db.readBytes(buf, 0, headerSize); - return buf; - } - - // juliana@212_8 - // juliana@210_2: now Litebase supports tables with ascii strings. - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Closes the table files. - * - * @param updatePos Indicates if finalPos must be re-calculated to shrink the file. - * @throws IOException If an internal method throws it. - */ - void close(boolean updatePos) throws IOException - { - ByteArrayStream tsmdBas = new ByteArrayStream(7); - DataStreamLB tsmdDs = new DataStreamLB(tsmdBas, useCrypto); // Creates a new stream. - byte[] buffer = tsmdBas.getBuffer(); - - // Stores the changeable information. - Convert.fill(buffer, 0, 4, 0); - buffer[0] = (byte)(useCrypto? (useOldCrypto? 1 : Table.USE_CRYPTO) : 0); - - tsmdDs.skipBytes(4); - tsmdDs.writeShort(headerSize); - - // The table format must also be saved. - tsmdDs.writeByte(isAscii? (Table.IS_ASCII | Table.IS_SAVED_CORRECTLY) : Table.IS_SAVED_CORRECTLY); - - writeMetaData(tsmdBas.getBuffer(), tsmdBas.getPos()); - - if (updatePos) - db.finalPos = rowCount * rowSize + headerSize; // Calculates .db used space: .db won't have zeros at the end. - - // Closes the files. - db.close(); - dbo.close(); - - dbo = db = null; - } - - /** - * Removes the table files. - * - * @throws IOException If an internal method throws it. - */ - public void remove() throws IOException - { - ((NormalFile)db).f.delete(); - ((NormalFile)dbo).f.delete(); - db = dbo = null; - } - - // juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Reads a value from a PlainDB. - * - * @param value The value to be read. - * @param offset The offset of the value in its row. - * @param colType The type of the value. - * @param stream The stream where the row data is stored. - * @param isTemporary Indicates if this is a result set table. - * @param isNull Indicates if the value is null. - * @param isTempBlob Indicates if the blob is being read for a temporary table. - * @return The total offset in the row. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - int readValue(SQLValue value, int offset, int colType, DataStreamLB stream, boolean isTemporary, boolean isNull, boolean isTempBlob) - throws IOException, InvalidDateException - { - if (isNull) // Only reads non-null values. - return offset; - - switch (colType) - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - if (isTemporary) - { - dbo.setPos(stream.readInt()); - value.asInt = dsdbo.readInt(); - value.asLong = dsdbo.readInt(); - PlainDB plainDB = ((Table)driver.htTables.get((int)value.asLong)).db; - plainDB.dbo.setPos(value.asInt); - value.asString = plainDB.loadString(); - } - else - { - if ((value.asInt = stream.readInt()) < dbo.finalPos && value.asInt >= 0) - { - dbo.setPos(value.asInt); // Reads the string position in the .dbo and sets its position. - value.asLong = Utils.subStringHashCode(name, 5); - value.asString = loadString(); - } - else - value.asString = ""; - } - break; - - case SQLElement.SHORT: - value.asShort = stream.readShort(); // Reads the short. - break; - - case SQLElement.INT: - value.asInt = stream.readInt(); - - // juliana@230_38: corrected possible indices problems when updating an integer field on JavaSE and BlackBerry. - if (((ByteArrayStream)stream.getStream()).getPos() == 4 && !isTemporary) // Is it the row id? - value.asInt = value.asInt & Utils.ROW_ID_MASK; // Masks out the attributes. - break; - - case SQLElement.LONG: - value.asLong = stream.readLong(); - break; - - case SQLElement.FLOAT: - value.asDouble = stream.readFloat(); - break; - - case SQLElement.DOUBLE: - value.asDouble = stream.readDouble(); - break; - - case SQLElement.DATE: - value.asInt = stream.readInt(); - break; - - case SQLElement.DATETIME: - value.asInt = stream.readInt(); // Reads the date. - value.asShort = stream.readInt(); // Reads the time. - break; - - case SQLElement.BLOB: // juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. - if (isTempBlob) // A blob is being read to a temporary table. - { - value.asInt = stream.readInt(); - value.asLong = Utils.subStringHashCode(name, 5); - } - else if (isTemporary) // A blob is being returned to the result set. - { - dbo.setPos(stream.readInt()); - int pos = dsdbo.readInt(); - PlainDB plainDB = ((Table)driver.htTables.get(dsdbo.readInt())).db; - plainDB.dbo.setPos(pos); - value.asBlob = new byte[plainDB.dsdbo.readInt()]; - if (value.asBlob.length > 0) - plainDB.dsdbo.readBytes(value.asBlob); - - } - else // A blob is being returned to the result set. - { - int pos = stream.readInt(); - - if (pos < dbo.finalPos && pos >= 0) - { - dbo.setPos(pos); // Reads the blob position in the .dbo and sets its position. - if (value.asInt != -1) - { - value.asBlob = new byte[dsdbo.readInt()]; // Creates the blob with its size. - if (value.asBlob.length > 0) // juliana@212_8: when reading a file, an exception must not be thrown when reading zero bytes. - dsdbo.readBytes(value.asBlob); // Reads the blob. - } - else - value.asInt = dsdbo.readInt(); - } - else - value.asInt = 0; - } - } - return offset + Utils.typeSizes[colType]; - - } - - // juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Writes a value to a table column. - * - * @param type The type of the column. - * @param value The value to be written. - * @param ds The buffer where the value is stored before going to the table. - * @param valueOk Indicates if the value is to be written. - * @param addingNewRecord Indicates if it is an update or an insert. - * @param colSize The column size of the value. - * @param offset The offset of the string or blob in an update. - * @param isTemporary Indicates if a temporary table is being used. - * @throws IOException If an internal method throws it. - */ - void writeValue(int type, SQLValue value, DataStreamLB ds, boolean valueOk, boolean addingNewRecord, int colSize, int offset, boolean isTemporary) - throws IOException - { - if (!valueOk) // Only writes non-null values and values being changed. - ds.skipBytes(Utils.typeSizes[type]); // If the value is null or is not updated, just skips its size. - else - switch (type) - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case SQLElement.CHARS_NOCASE: - case SQLElement.CHARS: - { - XFile dboFile = dbo; - - if (isTemporary) - { - if ((dboFile.finalPos + 8) > dboFile.size) - dboFile.growTo(dboFile.size + 8 * (rowInc > 16? rowInc : 16)); // If the .dbo is full, grows it. - dboFile.setPos(dboFile.finalPos); - ds.writeInt(dboFile.pos); - dsdbo.writeInt(value.asInt); - dsdbo.writeInt((int)value.asLong); - dboFile.finalPos = dboFile.pos; - } - else - { - // juliana@225_7. - // juliana@210_2: now Litebase supports tables with ascii strings. - int c = value.asString.length(), // The string that is bigger than its field definiton was already trimmed. - size = isAscii? c + 2 : (c << 1) + 2; // Computes the string size. - - // guich@201_8: grows using rowInc instead of 16 if rowInc > 16. - // juliana@201_20: only grows .dbo if it is going to be increased. - // juliana@212_7: The size of the string must be taken into consideration because it can be zero. - if ((dboFile.finalPos + size) > dboFile.size) - dboFile.growTo(dboFile.size + 2 + size * (rowInc > 16? rowInc : 16)); // If the .dbo is full, grows it. - - // juliana@202_21: Always writes the string at the end of the .dbo. This removes possible bugs when doing updates. - dboFile.setPos(dboFile.finalPos); - ds.writeInt(value.asInt = dboFile.pos); // The string position for an index and writes it in the .db - - // Writes the string to the buffer. - if (isAscii) // juliana@210_2: now Litebase supports tables with ascii strings. - { - String asString = value.asString; - int i = -1; - dsdbo.writeShort(c); // juliana@214_5: must trim ascii strings if they are longer than the field size definition. - while (++i < c) - dsdbo.writeByte(asString.charAt(i)); - } - else - dsdbo.writeChars(value.asString, c); - - dboFile.finalPos = dboFile.pos; // juliana@202_21: the final positon now is always the new positon. - } - break; - } - case SQLElement.SHORT: - ds.writeShort(value.asShort); - break; - - case SQLElement.DATE: - case SQLElement.INT: - ds.writeInt(value.asInt); - break; - - case SQLElement.LONG: - ds.writeLong(value.asLong); - break; - - case SQLElement.FLOAT: - ds.writeFloat(value.asDouble); - break; - - case SQLElement.DOUBLE: - ds.writeDouble(value.asDouble); - break; - - case SQLElement.DATETIME: - ds.writeInt(value.asInt); // Writes the date. - ds.writeInt(value.asShort); // Writes the time. - break; - - case SQLElement.BLOB: // juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. - { - XFile dboFile = dbo; - - if (isTemporary) // The position of a blob and its table is being written to the temporary table. - { - if ((dboFile.finalPos + 8) > dboFile.size) - dboFile.growTo(dboFile.size + 8 * (rowInc > 16? rowInc : 16)); // If the .dbo is full, grows it. - dboFile.setPos(dboFile.finalPos); - ds.writeInt(dboFile.pos); - dsdbo.writeInt(value.asInt); - dsdbo.writeInt((int)value.asLong); - dboFile.finalPos = dboFile.pos; - } - else - { - int size = Math.min(value.asBlob.length, colSize), // Trims a blob that is bigger than its field definiton. - oldPos = 0; - - // guich@201_8: grows using rowInc instead of 16 if rowInc > 16. - // juliana@201_20: only grows .dbo if it is going to be increased. - // juliana@212_7: The size of the blob must be taken into consideration because it can be zero. - if (addingNewRecord && (dboFile.finalPos + size + 4) >= (dboFile.size + 1)) - dboFile.growTo(dboFile.size + 4 + size * (rowInc > 16? rowInc : 16)); // If the .dbo is full, grows it. - - // It is an insert or the size of the blob is greater then the old, writes the blob at the end of the .dbo. - if (addingNewRecord) - dboFile.setPos(dboFile.finalPos); - else - { - oldPos = dboFile.pos; - dboFile.setPos(oldPos - offset); // The blob was read before. - } - ds.writeInt(dboFile.pos); // Writes its position in the ds. - dsdbo.writeInt(size); // Writes the blob size to .dbo. - if (size > 0) // juliana@212_8: when reading a file, an exception must not be thrown when writing zero bytes. - dsdbo.writeBytes(value.asBlob, 0, size); // Writes the blob itself to .dbo. - - // It is an insert or the size of the blob is greater then the old one, the final positon is the new positon. - if (addingNewRecord) - dboFile.finalPos = dboFile.pos; - - else // Otherwise, restores the old position. - dboFile.setPos(oldPos); - } - } - } - - } - - /** - * Tests if a record of a table is not deleted. - * - * @return false if the record is deleted; true otherwise. - * @throws IOException If an internal method throws it. - */ - boolean recordNotDeleted() throws IOException - { - bas.reset(); // Resets read position. - boolean notDeleted = (basds.readInt() & Utils.ROW_ATTR_MASK) != Utils.ROW_ATTR_DELETED; - bas.reset(); // Resets read position. - return notDeleted; - } - - String loadString() throws IOException - { - int length = dsdbo.readUnsignedShort(); - if (isAscii) // juliana@210_2: now Litebase supports tables with ascii strings. - { - byte[] buf = driver.buffer; - if (buf.length < length) - driver.buffer = buf = new byte[length]; - dsdbo.readBytes(buf, 0, length); - return length != 0? new String(buf, 0, length) : ""; // Reads the string. - } - else - { - char[] chars = driver.valueAsChars; - if (chars.length < length) - driver.valueAsChars = chars = new char[length]; - dsdbo.readChars(chars, length); - return length != 0? new String(chars, 0, length) : ""; // Reads the string. - } - } -} diff --git a/LitebaseSDK/src/java/litebase/PreparedStatement.java b/LitebaseSDK/src/java/litebase/PreparedStatement.java deleted file mode 100644 index a4b3ca5569..0000000000 --- a/LitebaseSDK/src/java/litebase/PreparedStatement.java +++ /dev/null @@ -1,808 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Represents a SQL Statement that can be prepared (compiled) once and executed many times with different parameter - * values. - */ -@Deprecated -public class PreparedStatement -{ - /** - * The SQL command expression. - */ - private String sqlExpression; - - /** - * The connection with Litebase. - */ - private LitebaseConnection driver; - - /** - * The type of the statement. It can be one of SQLElement.STMT_INSERT, - * SQLElement.STMT_UPDATE, SQLElement.STMT_DELETE, SQLElement.STMT_DROP, - * SQLElement.STMT_ALTER, and SQLElement.STMT_CREATE. - */ - private int type; - - /** - * The parameters for the prepared statement in string format. - */ - private String[] paramsAsStrs; // guich@566_15 - - /** - * The positions of the '?' in the sql string. - */ - private short[] paramsPos; - - /** - * Indicates if there are parameters in the SQL command. - */ - private boolean storeParams; // guich@566_15 - - /** - * The statement. - */ - private SQLStatement statement; - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - PreparedStatement() {} - - /** - * Prepares a SQL statement. - * - * @param newDriver The connection with Litebase. - * @param sql the SQL expression. - * - * @throws IOException If it an internal method throws it. - * @throws InvalidDateException If it an internal method throws it. - * @throws InvalidNumberException If it an internal method throws it. - * @throws SQLParseException If the sql string is empty. - */ - void prepare(LitebaseConnection newDriver, String sql) throws IOException, InvalidDateException, InvalidNumberException, SQLParseException - { - String tempSQL = sqlExpression = sql; // guich@503_9: Assign this, or it will become unusable. This is necessary for logging. - driver = newDriver; - type = SQLElement.CMD_NONE; - - LitebaseParser parser = null; // Parses and binds the statement. - - // juliana@202_5: removed possible NPE if there is a blank in the beginning of the sql command. - if ((tempSQL = sql.toLowerCase().trim()).startsWith("select")) - { - // Parses the SQL statement. - parser = new LitebaseParser(); - parser.tableList = new SQLResultSetTable[SQLElement.MAX_NUM_COLUMNS]; - parser.select = new SQLSelectClause(); - - // juliana@253_9: improved Litebase parser. - - // juliana@224_2: improved memory usage on BlackBerry. - LitebaseParser.parser(sql, parser, driver.lexer); // Does de parsing. - - type = parser.command; - } - else - { - if (tempSQL.startsWith("insert") || tempSQL.startsWith("update") || tempSQL.startsWith("delete")) - { - // Parses the SQL statement. - parser = new LitebaseParser(); - parser.tableList = new SQLResultSetTable[1]; - if (tempSQL.startsWith("insert") || tempSQL.startsWith("update")) - { - parser.fieldValues = new String[SQLElement.MAX_NUM_COLUMNS]; - parser.fieldNames = new String[SQLElement.MAX_NUM_COLUMNS]; - } - - // juliana@224_2: improved memory usage on BlackBerry. - LitebaseParser.parser(sql, parser, driver.lexer); // Does de parsing. - - type = parser.command; - } - - // juliana@212_1: corrected prepared statement parsing for create index. - if (tempSQL.startsWith("create")) - type = SQLElement.CMD_CREATE_TABLE; - else - if (tempSQL.length() == 0) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_SYNTAX_ERROR)); - } - - // juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first - // execution. - switch (type) // Gets the command in the SQL expression and calls the apropriate create statement. - { - case SQLElement.CMD_INSERT: // INSERT - statement = new SQLInsertStatement(parser, driver).litebaseBindInsertStatement(); - break; - case SQLElement.CMD_UPDATE: // UPDATE - statement = new SQLUpdateStatement(parser).litebaseBindUpdateStatement(driver); - SQLBooleanClause whereClause = ((SQLUpdateStatement)statement).whereClause; - if (whereClause != null) - whereClause.expressionTreeBak = whereClause.expressionTree.cloneTree(null); - break; - case SQLElement.CMD_DELETE: // DELETE - statement = new SQLDeleteStatement(parser).litebaseBindDeleteStatement(driver); - if ((whereClause = ((SQLDeleteStatement)statement).whereClause) != null) - whereClause.expressionTreeBak = whereClause.expressionTree.cloneTree(null); - break; - case SQLElement.CMD_SELECT: // SELECT - statement = new SQLSelectStatement(parser).litebaseBindSelectStatement(driver); - SQLSelectStatement selectStmt = (SQLSelectStatement)statement; - SQLColumnListClause orderByClause = selectStmt.orderByClause, - groupByClause = selectStmt.groupByClause; - SQLResultSetField[] fieldList; - short[] vi; // juliana@226_1 - int n; - - if (orderByClause != null) - { - n = orderByClause.fieldList.length; - fieldList = orderByClause.fieldList; - - // Saves the order by clause if there's no backup yet. - vi = orderByClause.fieldTableColIndexesBak = new short[n]; // juliana@226_1 - while (--n >= 0) - vi[n] = (short)fieldList[n].tableColIndex; // juliana@226_1 - } - - // juliana@226_14: corrected a bug that would make a prepared statement with group by not work correctly after the first execution. - if (groupByClause != null) - { - n = groupByClause.fieldList.length; - fieldList = groupByClause.fieldList; - - // Saves the order by clause if there's no backup yet. - vi = groupByClause.fieldTableColIndexesBak = new short[n]; // juliana@226_1 - while (--n >= 0) - vi[n] = (short)fieldList[n].tableColIndex; // juliana@226_1 - } - if ((whereClause = selectStmt.whereClause) != null) - whereClause.expressionTreeBak = whereClause.expressionTree.cloneTree(null); - } - - // If the statement is to be used as a prepared statement, it is possible to use log. - if (statement != null && LitebaseConnection.logger != null) - { - int length = Convert.numberOf(sql, '?'); // Finds the number of '?'. - - paramsPos = new short[length + 1]; // The array of positions of the '?' in the sql. - - if (length > 0) // If there is some '?' in the sql command string, there are parameters to be stored. - { - int i = sql.length(); - - paramsAsStrs = new String[length]; // Creates the array of parameters. - storeParams = true; - - // Marks the positions of the '?'. - paramsPos[length--] = (short)i; - while (--i >= 0) - if (sql.charAt(i) == '?') - paramsPos[length--] = (short)i; - - // juliana@201_15: The prepared statement parameters for logging must be set as "unfilled" when creating it. - Convert.fill(paramsAsStrs, 0, paramsAsStrs.length, "unfilled"); - } - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method executes a prepared SQL query and returns its ResultSet. - * - * @return The ResultSet of the SQL statement. - * @throws DriverException If an error occurs. This can be the case if the statement to be execute is not a select or an IOException - * occurs. - * @throws SQLParseException If an InvalidDateFormat or InvalidNumberFormat occurs. - */ - public ResultSet executeQuery() throws DriverException, SQLParseException - { - testPSState(); - - try - { - // juliana@225_9: removed a possible ClassCastException when passing an insert / update / delete to a PreparedStatement.executeQuery(). The - // correct exception is a DriverException. - if (type != SQLElement.CMD_SELECT) // The statement must be a select. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_QUERY_DOESNOT_RETURN_RESULTSET)); - - SQLSelectStatement selectStmt = (SQLSelectStatement)statement; // The select statement. - - selectStmt.allParamValuesDefined(); // All the parameters of the select statement must be defined. - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (LitebaseConnection.logger != null && !LitebaseConnection.logOnlyChanges) // If log is on, adds information to it. - synchronized (LitebaseConnection.logger) - { - LitebaseConnection.logger.logInfo(toStringBuffer()); - } - - resetWhereClause(selectStmt.whereClause); // guich@550_43: fixed problem when reusing the statement. - - // guich@554_37: tableColIndex may change between runs of a prepared statement with a sort field so we have to cache the tableColIndex of the - // order by fields. - resetColumnListClause(selectStmt.orderByClause); - - // juliana@226_14: corrected a bug that would make a prepared statement with group by not work correctly after the first execution. - resetColumnListClause(selectStmt.groupByClause); - - return selectStmt.litebaseDoSelect(driver); // Executes the query. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - } - - /** - * Resets an order by or group by clause because the tableColIndex may change between runs of a prepared statement with a sort field. - * So, it is necessary to cache the tableColIndex of order by fields. - * - * @param columnListClause the order by clause to be reseted. - */ - private void resetColumnListClause(SQLColumnListClause columnListClause) - { - if (columnListClause != null) // It may be null. - { - int n = columnListClause.fieldList.length; - SQLResultSetField[] fieldList = columnListClause.fieldList; - short[] vi = columnListClause.fieldTableColIndexesBak; - - while (--n >= 0) - fieldList[n].tableColIndex = vi[n]; - } - } - - // guich@554_13: this reset is needed by all statements. - /** - * Resets a where clause because the expression may change between runs of a prepared statement with a where clause. - * - * @param whereClause the were clause to be reseted. - */ - private void resetWhereClause(SQLBooleanClause whereClause) - { - if (whereClause != null) // guich@552_37: It may be null. - { - whereClause.appliedIndexesBooleanOp = whereClause.appliedIndexesCount = 0; - - // After the first use of this, the tree is nulled. So, a copy of it is gotten. - // guich@554_13: Use the expressionTreeBak as a condition instead of expressionTree (it should always be replaced after the first try). - // juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first - // execution. - whereClause.expressionTree = whereClause.expressionTreeBak.cloneTree(whereClause.expressionTree); - whereClause.resultSet = null; // Resets the result set. - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method executes a SQL INSERT, UPDATE, or DELETE statement. SQL statements that return nothing such - * as SQL DDL statements can also be executed. - * - * @return The result is either the row count for INSERT, UPDATE, or DELETE statements; or 0 for SQL - * statements that return nothing. - * @throws DriverException If an error occurs. This can happen if the query does not update the table or an IOException occurs. - * @throws SQLParseException If an InvalidDateException or an InvalidNumberExcepion occurs. - */ - public int executeUpdate() throws DriverException, SQLParseException - { - testPSState(); - - if (type == SQLElement.CMD_SELECT) // The statement musn't be a select. executeQuery() must be used instead. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_QUERY_DOESNOT_PERFORM_UPDATE)); - - // If there are undefined parameters (except for insert statements, where nulls are used instead, the statement must not be executed. - if (statement != null) - statement.allParamValuesDefined(); - - if (LitebaseConnection.logger != null) // If log is on, adds information to it. - synchronized (LitebaseConnection.logger) - { - LitebaseConnection.logger.logInfo(toStringBuffer()); - } - - try - { - switch (type) // Returns the number of rows affected or if the command was successfully executed. - { - case SQLElement.CMD_INSERT: - SQLInsertStatement insertStmt = (SQLInsertStatement)statement; - rearrangeNullsInTable(insertStmt.table, insertStmt, true); - insertStmt.table.convertStringsToValues(insertStmt.record); - insertStmt.litebaseDoInsert(driver); - return 1; - - case SQLElement.CMD_UPDATE: - SQLUpdateStatement updateStmt = (SQLUpdateStatement)statement; - rearrangeNullsInTable(updateStmt.rsTable.table, updateStmt, true); - resetWhereClause(updateStmt.whereClause); // guich@554_13 - updateStmt.rsTable.table.convertStringsToValues(updateStmt.record); - return updateStmt.litebaseDoUpdate(driver); - - case SQLElement.CMD_DELETE: - SQLDeleteStatement deleteStmt = (SQLDeleteStatement)statement; - resetWhereClause(deleteStmt.whereClause); // guich@_554_13 - return deleteStmt.litebaseDoDelete(driver); - - case SQLElement.CMD_CREATE_TABLE: - driver.execute(sqlExpression); - return 0; - - default: - return driver.executeUpdate(sqlExpression); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - } - - /** - * Stores the null values of prepared statement in the table. - * - * @param table The Table used in the prepared statement. - * @param stmt The prepared statement. - * @param isPreparedUpdateStmt Indicates if the prepared statement is an update prepared statement or not. - */ - private void rearrangeNullsInTable(Table table, SQLStatement stmt, boolean isPreparedStmt) - { - byte[] storeNulls; - boolean[] paramDefined; - SQLValue[] record; - short[] paramIndexes; // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - SQLValue value; - - if (stmt.type == SQLElement.CMD_INSERT) // Does the right cast. - { - SQLInsertStatement stat = (SQLInsertStatement)stmt; - storeNulls = stat.storeNulls; - record = stat.record; - paramIndexes = stat.paramIndexes; - paramDefined = stat.paramDefined; - } - else - { - SQLUpdateStatement stat = (SQLUpdateStatement)stmt; - storeNulls = stat.storeNulls; - record = stat.record; - paramIndexes = stat.paramIndexes; - paramDefined = stat.paramDefined; - } - - int len = record.length < paramIndexes.length? record.length : paramIndexes.length; // Finds the smallest length. - - while (--len >= 0) // If a parameter is not defined, set it as null. - if (!paramDefined[len] && (value = record[paramIndexes[len]]) != null) // juliana@201_17: NPE ocurred if record[paramIndexes[len]] == null. - value.isNull = true; - - table.storeNulls = storeNulls; - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java short value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public void setShort(int index, short value) - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setParamValue(index, value); - if (storeParams) // Only stores the parameter if there are parameters to be stored. - paramsAsStrs[index] = Convert.toString(value); - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java int value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public void setInt(int index, int value) - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setParamValue(index, value); - if (storeParams) // Only stores the parameter if there are parameters to be stored. - paramsAsStrs[index] = Convert.toString(value); - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java long value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public void setLong(int index, long value) - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setParamValue(index, value); - if (storeParams) // Only stores the parameter if there are parameters to be stored. - paramsAsStrs[index] = Convert.toString(value); - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java float value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public void setFloat(int index, double value) - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setParamValue(index, (float) value); - if (storeParams) // Only stores the parameter if there are parameters to be stored. - paramsAsStrs[index] = Convert.toString(value); - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java double value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public void setDouble(int index, double value) - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setParamValue(index, value); - if (storeParams) // Only stores the parameter if there are parameters to be stored. - paramsAsStrs[index] = Convert.toString(value); - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java String value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. DO NOT SURROUND IT WITH '!. - * @throws SQLParseException If an InvalidNumberException or an InvalidDateException is thrown. - */ - public void setString(int index, String value) throws SQLParseException - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - try - { - statement.setParamValue(index, value); - } - catch (InvalidNumberException exception) - { - throw new SQLParseException(exception); - } - catch (InvalidDateException exception) - { - throw new SQLParseException(exception); - } - - if (storeParams) // Only stores the parameter if there are parameters to be stored. - { - if (value == null) - paramsAsStrs[index] = null; - else - { - StringBuffer sbuf = driver.sBuffer; - - sbuf.setLength(0); - paramsAsStrs[index] = sbuf.append('\'').append(value).append('\'').toString(); - } - } - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given array of bytes as a blob. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public void setBlob(int index, byte[] value) - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setParamValue(index, value); - if (storeParams) // A blob can't be stored as a string. - { - if (value != null) - paramsAsStrs[index] = "[BLOB]"; - else - paramsAsStrs[index] = null; - } - } - } - - // rnovais@570_17: formats the Date d as a string "YYYY/MM/DD" and calls setString(). - /** - * This method sets the specified parameter from the given Java Date value formated as "YYYY/MM/DD"
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, - * according to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, - * can generate a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date - * format parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param date The value of the parameter, which can be null. - */ - public void setDate(int index, Date date) // juliana@220_6: setDate() and setDateTime() must accept null values. - { - setString(index, date == null? null : date.toString(Settings.DATE_YMD)); // Formats the date so that it can be accepted by SQL. - } - - // rnovais@_570_17: formats the Date d and the Time t as a string "YYYY/MM/DD HH:MM:SS:ZZZ" and calls setString(). - /** - * This method sets the specified parameter from the given Java DateTime value formated as "YYYY/MM/DD HH:MM:SS:ZZZ".
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, - * according to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, - * can generate a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date - * format parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param date The value of the parameter, which can be null. - */ - public void setDateTime(int index, Date date) // juliana@220_6: setDate() and setDateTime() must accept null values. - { - setString(index, date == null? null : date.toString(Settings.DATE_YMD)); // Formats the date so that it can be accepted by SQL. - } - - // rnovais@570_56 - /** - * Formats the Time t into a string "YYYY/MM/DD HH:MM:SS:ZZZ" - * - * @param index The index of the parameter value to be set, starting from 0. - * @param time The value of the parameter, which can be null. - */ - public void setDateTime(int index, Time time) // juliana@220_6: setDate() and setDateTime() must accept null values. - { - // Formats the time so that it can be accepted by SQL. - StringBuffer sbuf = driver.sBuffer; - - sbuf.setLength(0); - if (time == null) - setString(index, null); - else - setString(index, sbuf.append(time.year).append('/').append(time.month).append('/').append(time.day).append(' ').append(time.hour) - .append(':').append(time.minute).append(':').append(time.second).append(':').append(time.millis).toString()); - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@223_3: PreparedStatement.setNull() now works for blobs. - /** - * Sets null in a given field. This can be used to set any column type as null. It must be just remembered that a parameter in a where clause - * can't be set to null. - * - * @param index The index of the parameter value to be set as null, starting from 0. - * @throws SQLParseException If an InvalidNumberException or an InvalidDateException is thrown. - */ - public void setNull(int index) throws SQLParseException - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - statement.setNull(index); - - if (storeParams) // Only stores the parameter if there are parameters to be stored. - paramsAsStrs[index] = null; // The string is null. - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method clears all of the input parameters that have been set on this statement. - */ - public void clearParameters() - { - testPSState(); - - if (statement != null) // Only sets the parameter if the statement is not null. - { - if (storeParams) // Only stores the parameter if there are parameters to be stored. - Convert.fill(paramsAsStrs, 0, paramsAsStrs.length, "unfilled"); - statement.clearParamValues(); - } - } - - // juliana@253_20: added PreparedStatement.close(). - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the sql used in this statement. If logging is disabled, returns the sql without the arguments. If logging is enabled, returns the real - * sql, filled with the arguments. - * - * @return the sql used in this statement. - */ - public String toString() - { - testPSState(); - - if (storeParams) - { - StringBuffer sb = driver.sBuffer; - String sql = sqlExpression; - short[] poss = paramsPos; - String[] strs = paramsAsStrs; - int n = strs.length, - i = -1; - - sb.setLength(0); - sb.append("PREP: "); - appendSubString(sb, sql, 0, poss[0]); - - while (++i < n) - { - sb.append(strs[i]); - appendSubString(sb, sql, poss[i] + 1, poss[i + 1]); - } - return sb.toString(); - } - return sqlExpression; - } - - /** - * Returns the sql used in this statement as a StringBuffer. If logging is disabled, returns the sql without the arguments. If logging is enabled, - * returns the real sql, filled with the arguments. There is also logger information prepended, since this method is only used for logging. - * - * @return the sql used in this statement as a StringBuffer. - */ - private StringBuffer toStringBuffer() - { - StringBuffer sb = driver.sBuffer; - sb.setLength(0); - - if (storeParams) - { - - String sql = sqlExpression; - short[] poss = paramsPos; - String[] strs = paramsAsStrs; - int n = strs.length, - i = -1; - - sb.append("PREP: "); - appendSubString(sb, sql, 0, poss[0]); - - while (++i < n) - { - sb.append(strs[i]); - appendSubString(sb, sql, poss[i] + 1, poss[i + 1]); - } - return sb; - } - sb.append(sqlExpression); - return sb; - } - - /** - * Appends characters from a string in a string buffer. - * - * @param strBuffer The string buffer. - * @param string The string to have some of the characters appended at the end of the string buffer. - * @param initialPos The position of the first character to be appended. - * @param endPos The fist position of the character that won't be appended. - */ - private void appendSubString(StringBuffer strBuffer, String string, int initialPos, int endPos) - { - while (initialPos < endPos) - strBuffer.append(string.charAt(initialPos++)); - } - - // juliana@253_20: added PreparedStatement.close(). - /** - * Tests if the driver is closed or the prepared statement is closed. - * - * @throws IllegalStateException If one of them is closed. - */ - private void testPSState() throws IllegalStateException - { - if (driver.htTables == null) // The connection with Litebase can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - if (!driver.htPS.exists(sqlExpression)) // The prepared statement can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PREPARED_CLOSED)); - } - - // juliana@253_20: added PreparedStatement.close(). - /** - * Closes a prepared statement. - * - * @throws IllegalStateException If the driver or the prepared statement is closed. - */ - public void close() throws IllegalStateException - { - if (driver.htTables == null) // The connection with Litebase can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - if (driver.htPS.remove(sqlExpression.hashCode()) == null) // The prepared statement can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PREPARED_CLOSED)); - } - - // juliana@253_21: added PreparedStatement.isValid(). - /** - * Indicates if a prepared statement is valid or not: the driver is open and its SQL is in the hash table. - * - * @return true if the prepared statement is valid; false, otherwise. - */ - public boolean isValid() - { - return (driver.htTables != null && driver.htPS.exists(sqlExpression)); - } -} diff --git a/LitebaseSDK/src/java/litebase/PreparedStatement4D.java b/LitebaseSDK/src/java/litebase/PreparedStatement4D.java deleted file mode 100644 index f8cee73d58..0000000000 --- a/LitebaseSDK/src/java/litebase/PreparedStatement4D.java +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Native class that represents a SQL Statement that can be prepared (compiled) once and executed many times with different parameter values. - */ -@Deprecated -public class PreparedStatement4D -{ - /** - * The SQL command expression. - */ - String sqlExpression; - - /** - * The Litebase connection. - */ - Object driver; - - /** - * The type of the statement. It can be one out of SQLElement.STMT_INSERT, SQLElement.STMT_UPDATE, - * SQLElement.STMT_DELETE, SQLElement.STMT_DROP, SQLElement.STMT_ALTER, - * and SQLElement.STMT_CREATE. - */ - int type; - - /** - * The parameters for the prepared statement in string format. - */ - long paramsAsStrs; // guich@566_15 - - /** - * The positions of the '?' in the sql string. - */ - long paramsPos; - - /** - * The length of the parameters as strings; - */ - long paramsLength; - - /** - * Indicates how many parameters are in the SQL command. - */ - int storedParams; // guich@566_15 - - /** - * The statement. - */ - long statement; - - /** - * A flag that indicates that this class has already been finalized. - */ - boolean dontFinalize; - - // juliana@222_8: corrected a bug that could collect strings and blobs passed to a prepared statement, causing invalid data insertion/update on - // Palm, Windows 32, Windows CE, iPhone, and Android. - /** - * An array of objects hooked in the prepared statement record. - */ - Object[] ObjParams; - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - private PreparedStatement4D() {} - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method executes a prepared SQL query and returns its ResultSet. - * - * @return The ResultSet of the SQL statement. - * @throws DriverException If the statement to be execute is not a select or there are undefined parameters. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native ResultSet executeQuery() throws DriverException, OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method executes a SQL INSERT, UPDATE, or DELETE statement. SQL statements that return nothing such - * as SQL DDL statements can also be executed. - * - * @return The result is either the row count for INSERT, UPDATE, or DELETE statements; or 0 for SQL - * statements that return nothing. - * @throws DriverException If the query does not update the table or there are undefined parameters. - */ - public native int executeUpdate() throws DriverException; - - /** - * This method sets the specified parameter from the given Java short value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public native void setShort(int index, short value); - - /** - * This method sets the specified parameter from the given Java int value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public native void setInt(int index, int value); - - /** - * This method sets the specified parameter from the given Java long value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public native void setLong(int index, long value); - - /** - * This method sets the specified parameter from the given Java float value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public native void setFloat(int index, double value); - - /** - * This method sets the specified parameter from the given Java double value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - */ - public native void setDouble(int index, double value); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java String value. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. DO NOT SURROUND IT WITH '!. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native void setString(int index, String value) throws OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given array of bytes as a blob. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param value The value of the parameter. - * @throws SQLParseException If the parameter to be set is in the where clause. - */ - public native void setBlob(int index, byte[] value) throws SQLParseException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method sets the specified parameter from the given Java Date value formated as "YYYY/MM/DD"
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, - * according to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, - * can generate a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date - * format parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param date The value of the parameter. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native void setDate(int index, Date date) throws OutOfMemoryError; - - /** - * This method sets the specified parameter from the given Java DateTime value formated as "YYYY/MM/DD HH:MM:SS:ZZZ".
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, - * according to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, - * can generate a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date - * format parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param index The index of the parameter value to be set, starting from 0. - * @param date The value of the parameter. - */ - public native void setDateTime(int index, Date date); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Formats the Time t into a string "YYYY/MM/DD HH:MM:SS:ZZZ" - * - * @param index The index of the parameter value to be set, starting from 0. - * @param time The value of the parameter. - * @throws OutOfMemoryError If a memory allocation fails. - */ - public native void setDateTime(int index, Time time) throws OutOfMemoryError; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@223_3: PreparedStatement.setNull() now works for blobs. - /** - * Sets null in a given field. This can be used to set any column type as null. It must be just remembered that a parameter in a where clause - * can't be set to null. - * - * @param index The index of the parameter value to be set as null, starting from 0. - * @throws SQLParseException If the parameter to be set as null is in the where clause. - */ - public native void setNull(int index) throws SQLParseException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * This method clears all of the input parameters that have been set on this statement. - */ - public native void clearParameters(); - - /** - * Returns the sql used in this statement. If logging is disabled, returns the sql without the arguments. If logging is enabled, returns the real - * sql, filled with the arguments. - * - * @returns the sql used in this statement. - */ - public native String toString(); - - // juliana@253_20: added PreparedStatement.close(). - /** - * Closes a prepared statement. - */ - public native void close(); - - // juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - - // juliana@253_21: added PreparedStatement.isValid(). - /** - * Indicates if a prepared statement is valid or not: the driver is open and its SQL is in the hash table. - * - * @return true if the prepared statement is valid; false, otherwise. - */ - public native boolean isValid(); -} diff --git a/LitebaseSDK/src/java/litebase/PrimaryKeyViolationException.java b/LitebaseSDK/src/java/litebase/PrimaryKeyViolationException.java deleted file mode 100644 index ae15391128..0000000000 --- a/LitebaseSDK/src/java/litebase/PrimaryKeyViolationException.java +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be thrown by LitebaseConnection.executeUpdate, when an update in a table can not be completed because it violates - * the primary key rule. It can also be thrown when adding a primary key to a table if there is a duplicated or null in a primary key. It is an - * unchecked Exception (can be thrown any time). - */ -@Deprecated -public class PrimaryKeyViolationException extends RuntimeException -{ - /** - * Constructs a new PrimaryKeyViolationException exception with the specified detail message. - * - * @param message the detail message. - */ - PrimaryKeyViolationException(String message) - { - super(message); - } -} diff --git a/LitebaseSDK/src/java/litebase/PrimaryKeyViolationException4D.java b/LitebaseSDK/src/java/litebase/PrimaryKeyViolationException4D.java deleted file mode 100644 index 6dae8df8f7..0000000000 --- a/LitebaseSDK/src/java/litebase/PrimaryKeyViolationException4D.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be thrown by LitebaseConnection.executeUpdate, when an update in a table can not be completed because it violates - * the primary key rule. It can also be thrown when adding a primary key to a table if there is a duplicated or null in a primary key. It is an - * unchecked Exception (can be thrown any time). - */ -@Deprecated -public class PrimaryKeyViolationException4D extends RuntimeException -{ -} diff --git a/LitebaseSDK/src/java/litebase/ReservedHashtable.java b/LitebaseSDK/src/java/litebase/ReservedHashtable.java deleted file mode 100644 index 6199c5222d..0000000000 --- a/LitebaseSDK/src/java/litebase/ReservedHashtable.java +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Hash table for Litebase SQL reserved words. - */ -@Deprecated -class ReservedHashtable -{ - /** - * The hash table data. - */ - private Entry[] table; - - /** - * Constructs a new, empty hash table with the specified initial capacity. - * - * @param initialCapacity The number of elements the hash table will probably end with. - */ - ReservedHashtable(int initialCapacity) - { - table = new Entry[initialCapacity]; - } - - /** - * Returns the value to which the specified hash is mapped in this hash table. - * That is, return the token code if a reserved word is passed as a key which matches the identifier found. - * - * @param hash The key hash in the hash table. - * @param string The identifier with the passed hash code inside a StringBuffer. - * @return The value to which the key is mapped in this hash table; -1 if the key is not mapped to any value in this hash table. - */ - int get(int hash, StringBuffer string) - { - int index = (hash & 0x7FFFFFFF) % table.length; - for (Entry entry = table[index]; entry != null; entry = entry.next) - if (entry.hash == hash && equalsSB(entry.key, string)) - return entry.value; - return -1; - } - - /** - * Maps the specified key to the specified value in this hash table. That is, puts the pair in the hash table. - * - * @param key The hash table key. - * @param value The value. - */ - void put(String key, int value) - { - Entry[] tab = table; - int hash = key.hashCode(), // flsobral@tc100b4_23: this operation throws NPE if key is null; no need to explicitly test that. - index = (hash & 0x7FFFFFFF) % tab.length; - - // Creates the new entry. - Entry entry = new Entry(); - entry.hash = hash; - entry.key = key; - entry.value = value; - entry.next = tab[index]; - tab[index] = entry; - } - - /** - * Compares the contents of a String and a StringBuffer. - * - * @param string The String. - * @param sBuffer The StringBuffer. - * @return true if the contents are the same; false, otherwise. - */ - private boolean equalsSB(String string, StringBuffer sBuffer) - { - int i = string.length(); - - if (i != sBuffer.length()) - return false; - - while (--i >= 0) - if (string.charAt(i) != sBuffer.charAt(i)) - return false; - return true; - } -} diff --git a/LitebaseSDK/src/java/litebase/ResultSet.java b/LitebaseSDK/src/java/litebase/ResultSet.java deleted file mode 100644 index 1a55d97062..0000000000 --- a/LitebaseSDK/src/java/litebase/ResultSet.java +++ /dev/null @@ -1,1446 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * This class represents a set or rows resulting from a LitebaseConnection.executeQuery() method call. - * Here's an example: - * - *
    - * ResultSet rs = driver.executeQuery("select name, salary, age from person");
    - * while (rs.next())
    - *    Vm.debug(pad(rs.getString("name"), 32) + pad(rs.getString("salary"), 16) 
    - *                                                     + rs.getInt("age") + " years");
    - * 
    - * - * Result sets cannot be constructed directly; instead, you must issue a sql to the driver. - */ -@Deprecated -public class ResultSet -{ - /** - * Current record position being read. - */ - int pos; - - /** - * Number of records of the result set. - */ - int lastRecordIndex; - - /** - * When rowsBitmap is generated, indicates what is the boolean relationship between the rows marked in the bitmap and any remaining - * WHERE clause. - */ - int rowsBitmapBoolOp; - - /** - * The number of columns in this result set. - */ - int columnCount; - - /** - * The index of the correspodent result set. - */ - int indexRs = -1; - - /** - * Counts the number of indices when running generateIndexedRowsMap(). - */ - int indexCount; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - /** - * The number of valid records of this result set. - */ - int answerCount = -1; - - /** - * Indicates if it is a select of the form select * from table or not. - */ - boolean isSimpleSelect; - - /** - * The associated table for the result set. - */ - Table table; - - /** - * A map with rows that satisfy totally or partially the query WHERE clause; generated using the table indices. - */ - IntVector rowsBitmap; - - /** - * An auxiliary map with rows that satisfy totally or partially the query WHERE clause; generated from the table indices. - */ - IntVector auxRowsBitmap; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * The indices used in this result set. - */ - IntVector indices = new IntVector(3); - - /** - * An array with the number of decimal places that is used to format float and double values, when being retrieved using - * the getString() method. This can be set at runtime by the user, and it is -1 as default. - */ - byte[] decimalPlaces; - - /** - * A map with rows that satisfy totally the query WHERE clause. - */ - byte[] allRowsBitmap; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * The WHERE clause associated with the result set. - */ - SQLBooleanClause whereClause; - - /** - * The returned fields of the select used for result set meta data. - */ - SQLResultSetField[] fields; - - /** - * Contains the hash of the all possible colunm names in the select statement. - */ - IntHashtable htName2index; - - /** - * The value from the result set that will be read. - */ - private SQLValue vrs = new SQLValue(); - - /** - * The connection with Litebase. - */ - LitebaseConnection driver; // juliana@220_3 - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - ResultSet () {} - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the meta data for this result set. - * - * @return The meta data for this result set. - */ - public ResultSetMetaData getResultSetMetaData() - { - verifyResultSet(); // The driver or result set can't be closed. - return new ResultSetMetaData(this); - } - - /** - * Closes a result set. Releases all memory allocated for this object. Its a good idea to call this when you no longer needs it, but it is also - * called by the GC when the object is no longer in use. - * - * @throws IllegalStateException if the result set is closed. - */ - public void close() throws IllegalStateException - { - // juliana@211_4: solved bugs with result set dealing. - if (table == null) // The result set can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RESULTSET_CLOSED)); - - table = null; // To close the resultSet, just sets its table to null. - } - - /** - * Places the cursor before the first record. - */ - public void beforeFirst() - { - verifyResultSet(); // The driver or result set can't be closed. - pos = -1; - } - - /** - * Places the cursor after the last record. - */ - public void afterLast() - { - verifyResultSet(); // The driver or result set can't be closed. - pos = lastRecordIndex + 1; - } - - /** - * Places the cursor in the first record of the result set. - * - * @return true if it was possible to place the cursor in the first record; false, otherwise. - */ - public boolean first() - { - verifyResultSet(); // The driver or result set can't be closed. - pos = -1; // Sets the position before the first record. - if (next()) // Reads the first record. - return true; - pos = -1; // guich@105: Sets the record to -1 if it can't read the first position. - return false; - } - - /** - * Places the cursor in the last record of the result set. - * - * @return true if it was possible to place the cursor in the last record; false, otherwise. - */ - public boolean last() - { - verifyResultSet(); // The driver or result set can't be closed. - pos = lastRecordIndex + 1; // Sets the position after the last record. - if (prev()) // Reads the last record. - return true; - pos = -1; // guich@105: Sets the record to -1 if it can't read the last position. - return false; - } - - /** - * Gets the next record of the result set. - * - * @return true if there is a next record to go to in the result set; false, otherwise. - * @throws DriverException If an IOException occurs. - */ - public boolean next() throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - try - { - byte[] rowsBitmap = allRowsBitmap; - Table tableAux = table; - PlainDB plainDB = tableAux.db; - DataStreamLB basds = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - ByteArrayStream bas = plainDB.bas; - int last = lastRecordIndex; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap != null) - { - int i = pos; - - while (i++ < last) - if ((rowsBitmap[i >> 3] & (1 << (i & 7))) != 0) - { - plainDB.read(pos = i); - tableAux.readNullBytesOfRecord(0, false, 0); - return true; - } - return false; - } - - // juliana@114_10: if it is a simple select, there may be deleted rows, which must be skiped. - if (tableAux.deletedRowsCount > 0) - { - boolean isDeleted = false; // Indicates if it was deleted. - int lastPos = pos; // juliana@211_4: solved bugs with result set dealing. - - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (pos++ < last) - { - plainDB.read(pos); - if (!(isDeleted = (basds.readInt() & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED)) - break; - } - - if (pos <= last && !isDeleted) // Sets the position after the last record. - { - bas.setPos(0); - tableAux.readNullBytesOfRecord(0, false, 0); - return true; - } - - // juliana@211_4: solved bugs with result set dealing. - // If there are no more rows to be returned, the last valid one must be used. - plainDB.read(pos = lastPos); - bas.setPos(0); - tableAux.readNullBytesOfRecord(0, false, 0); - return false; - } - - if (pos < last) // Only returns a row as read if it before the end of the temporary table. - { - plainDB.read(++pos); - tableAux.readNullBytesOfRecord(0, false, 0); - return true; - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - return false; - } - - /** - * Returns the previous record of the result set. - * - * @return true if there is a previous record to go to in the result set; false, otherwise. - * @throws DriverException If an IOException occurs. - */ - public boolean prev() throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - try - { - byte[] rowsBitmap = allRowsBitmap; - Table tableAux = table; - PlainDB plainDB = tableAux.db; - DataStreamLB basds = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - ByteArrayStream bas = plainDB.bas; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap != null) - { - int i = pos; - - while (i-- > 0) - if ((rowsBitmap[i >> 3] & (1 << (i & 7))) != 0) - { - plainDB.read(pos = i); - tableAux.readNullBytesOfRecord(0, false, 0); - return true; - } - return false; - } - - // juliana@114_10: if it is a simple select, there may be deleted rows, which must be skiped. - if (tableAux.deletedRowsCount > 0) - { - boolean isDeleted = false; // Indicates if it was deleted. - int lastPos = pos; // juliana@211_4: solved bugs with result set dealing. - - while (pos-- > 0) // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - { - plainDB.read(pos); - if (!(isDeleted = (basds.readInt() & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED)) - break; - } - - if (pos >= 0 && !isDeleted) // Only returns a row as read if it is not deleted. - { - bas.setPos(0); - tableAux.readNullBytesOfRecord(0, false, 0); - return true; - } - - // juliana@211_4: solved bugs with result set dealing. - // If there are no more rows to be returned, the last valid one must be used. - plainDB.read(pos = lastPos); - bas.setPos(0); - tableAux.readNullBytesOfRecord(0, false, 0); - return false; - } - - if (pos > 0) // Only returns a row as read if it is after the beginning of the temporary table. - { - plainDB.read(--pos); - tableAux.readNullBytesOfRecord(0, false, 0); - return true; - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - return false; - } - - /** - * Given the column index (starting from 1), returns a short value that is represented by this column. Note that it is only possible to request - * this column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public short getShort(int colIdx) - { - return getFromIndex(colIdx, SQLElement.SHORT)? (short)vrs.asShort : 0; - } - - /** - * Given the column name (case insensitive), returns a short value that is represented by this column. Note that it is only possible to request - * this column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. This - * method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public short getShort(String colName) - { - return getFromName(colName, SQLElement.SHORT)? (short)vrs.asShort : 0; - } - - /** - * Given the column index (starting from 1), returns an integer value that is represented by this column. Note that it is only possible to request - * this column as integer if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public int getInt(int colIdx) - { - return getFromIndex(colIdx, SQLElement.INT)? vrs.asInt : 0; - } - - /** - * Given the column name (case insensitive), returns an integer value that is represented by this column. Note that it is only possible to request - * this column as integer if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public int getInt(String colName) - { - return getFromName(colName, SQLElement.INT)? vrs.asInt : 0; - } - - /** - * Given the column index (starting from 1), returns a long value that is represented by this column. Note that it is only possible to request - * this column as long if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public long getLong(int colIdx) - { - return getFromIndex(colIdx, SQLElement.LONG)? vrs.asLong : 0; - } - - /** - * Given the column name (case insensitive), returns a long value that is represented by this column. Note that it is only possible to request - * this column as long if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public long getLong(String colName) - { - return getFromName(colName, SQLElement.LONG)? vrs.asLong : 0; - } - - /** - * Given the column index (starting from 1), returns a float value that is represented by this column. Note that it is only possible to request - * this column as float if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public double getFloat(int colIdx) - { - return getFromIndex(colIdx, SQLElement.FLOAT)? vrs.asDouble : 0; - } - - /** - * Given the column name (case insensitive), returns a float value that is represented by this column. Note that it is only possible to request t - * his column as float if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public double getFloat(String colName) - { - return getFromName(colName, SQLElement.FLOAT)? vrs.asDouble : 0; - } - - /** - * Given the column index (starting from 1), returns a double value that is represented by this column. Note that it is only possible to request - * this column as double if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public double getDouble(int colIdx) - { - return getFromIndex(colIdx, SQLElement.DOUBLE)? vrs.asDouble : 0; - } - - /** - * Given the column name (case insensitive), returns a double value that is represented by this column. Note that it is only possible to request - * this column as double if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public double getDouble(String colName) - { - return getFromName(colName, SQLElement.DOUBLE)? vrs.asDouble : 0; - } - - /** - * Given the column index (starting from 1), returns a char array that is represented by this column. Note that it is only possible to request - * this column as a char array if it was created as a string. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public char[] getChars(int colIdx) - { - return getFromIndex(colIdx, SQLElement.CHARS)? vrs.asString.toCharArray() : null; - } - - /** - * Given the column name (case insensitive), returns a char array that is represented by this column. Note that it is only possible to request - * this column as a char array if it was created as a string. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public char[] getChars(String colName) - { - return getFromName(colName, SQLElement.CHARS)? vrs.asString.toCharArray() : null; - } - - /** - * Given the column index (starting from 1), returns a byte (blob) array that is represented by this column. Note that it is only possible to - * request this column as a blob if it was created this way. - * - * @param colIdx the column index. - * @return the column value; if the value is SQL NULL, the value returned is null. - */ - public byte[] getBlob(int colIdx) - { - return getFromIndex(colIdx, SQLElement.BLOB)? vrs.asBlob : null; - } - - /** - * Given the column name (case insensitive), returns a byte array (blob) that is represented by this column. Note that it is only possible to - * request this column as a blob if it was created this way. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public byte[] getBlob(String colName) - { - return getFromName(colName, SQLElement.BLOB)? vrs.asBlob : null; - } - - /** - * Given the column index (starting from 1), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is null - */ - public String getString(int colIdx) - { - return getFromIndex(colIdx, SQLElement.UNDEFINED)? vrs.asString : null; - } - - /** - * Given the column name (case insensitive), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. This - * method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is null - */ - public String getString(String colName) - { - return getFromName(colName, SQLElement.UNDEFINED)? vrs.asString : null; - } - - /** - * Starting from the current cursor position, it reads all result set rows that are being requested. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. This method moves the cursor - * to the row after the last one fetched. - * - * @param count The number of rows to be fetched, or -1 for all. - * @return A matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column elements of the - * first row. Returns null if there's no more element to be fetched. Double/float values will be formatted using - * setDecimalPlaces() settings. If the value is SQL NULL, the value returned is null. - * @throws DriverException If an IOException occurs or the result set is in an invalid state. - * @throws IllegalArgumentException If count is less then -1. - */ - public String[][] getStrings(int count) throws DriverException, IllegalArgumentException - { - verifyResultSet(); // The driver or result set can't be closed. - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a - // DriverException. - if (count < -1) // The number of rows returned can't be negative. - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RS_INV_POS)); - - if (count == -1) // If count = -1, fetch all rows of the result set. - count = 0xFFFFFFF; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - Table tableAux = table; - boolean isTemporary = (allRowsBitmap == null && !isSimpleSelect); - short[] offsets = tableAux.columnOffsets; - byte[] types = tableAux.columnTypes; - byte[] nulls = tableAux.columnNulls[0]; - byte[] decimals = decimalPlaces; - SQLResultSetField[] rsFields = fields; - SQLResultSetField field; - String[] row; - SQLValue value = vrs; - int last = lastRecordIndex, - rows = 0, // The number of rows to be fetched. - - // juliana@114_10: skips the rowid. - - // juliana@211_3: the string matrix size can't take into consideration rows that are before the result set pointer. - records = last + 1 - pos, // The records that are fetched, skipping the deleted rows. - - i, - column, - columns = rsFields.length; - String[][] strings = new String[count < records ? count : records][]; // Stores the strings. - - if (count == 0) - return strings; - - if (pos < 0 || pos > last) // The position of the cursor must be greater then 0 and less then the last position. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RS_INV_POS)); - - int validRecords = 0; // juliana@211_4: solved bugs with result set dealing. - - try - { - do - { - row = strings[rows++] = new String[columns]; // juliana@201_19: Does not consider rowid. - i = columns; - while (--i >= 0) - { - field = rsFields[i]; - if (isTemporary) - column = i; - else - column = field.parameter == null? field.tableColIndex : field.parameter.tableColIndex; - - // Only reads the column if it is not null and not a BLOB. - if ((nulls[column >> 3] & (1 << (column & 7))) == 0 && types[column] != SQLElement.BLOB) - { - // juliana@220_3 - tableAux.readValue(value, offsets[column], types[column], false, false); - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - // juliana@270_31: Corrected bug of ResultSet.getStrings() don't working properly when there is a data function in the columns - // being fetched. - if (field.isDataTypeFunction) - applyDataTypeFunction(field, SQLElement.UNDEFINED); - else - createString(types[column], decimals == null? - 1: decimals[column]); - - row[i] = value.asString; - } - } - validRecords++; // juliana@211_4: solved bugs with result set dealing. - } while (--count > 0 && next()); // Continues until there are rows to be read and the number of rows read is not the desired. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - - // juliana@211_4: solved bugs with result set dealing. - // The strings matrix can't have nulls at the end. - if (strings.length > validRecords) - { - String[][] stringsAux = new String[validRecords][]; - Vm.arrayCopy(strings, 0, stringsAux, 0, validRecords); - return stringsAux; - } - - return strings; // Returns the string matrix. - } - - /** - * Starting from the current cursor position, it reads all result set rows of the result set. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. This method moves the cursor - * to the row after the last one fetched. - * - * @return A matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column elements of the - * first row. Returns null if there's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL, the value returned is null. - */ - public String[][] getStrings() - { - return getStrings(-1); - } - - /** - * Given the column index (starting from 1), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE). - * - * @param colIdx The column index. - * @return The column value as a Date object; if the value is SQL NULL, the value returned is null. - * null never occurs. - */ - public Date getDate(int colIdx) // rnovais@567_3 - { - try // Tries to transform a date fetched as an int into a date. - { - return getFromIndex(colIdx, SQLElement.DATE)? new Date(vrs.asInt) : null; - } - catch (InvalidDateException exception) - { - return null; - } - } - - /** - * Given the column name (case insensitive), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE). This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value as a Date object; if the value is SQL NULL, the value returned is null. - * null never occurs. - */ - public Date getDate(String colName) // rnovais@567_3 - { - try // Tries to transform a date fetched as an int into a date. - { - return getFromName(colName, SQLElement.DATE)? new Date(vrs.asInt): null; - } - catch (InvalidDateException exception) - { - return null; - } - } - - /** - * Given the column index (starting from 1), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a datetime if it was created this way. - * - * @param colIdx The colum index, starting from 1. - * @return The column value as a Time object; if the DATETIME value is SQL NULL, the value returned is - * null. - */ - public Time getDateTime(int colIdx) // rnovais@567_3 - { - return getFromIndex(colIdx, SQLElement.DATETIME)? new Time(vrs.asInt, vrs.asShort) : null; - } - - /** - * Given the column name (case insensitive), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a datetime if it was created this way. This method is slightly slower then the - * method that accepts a column index. - * - * @param colName The colum name. - * @return The column value as a Time object; if the DATETIME value is SQL NULL, the value returned is - * null. - */ - public Time getDateTime(String colName) // rnovais@567_3 - { - return getFromName(colName, SQLElement.DATETIME) ? new Time(vrs.asInt, vrs.asShort) : null; - } - - /** - * Places this result set cursor at the given absolute row. This is the absolute physical row of the table. This method is usually used to restore - * the row at a previous row got with the getRow() method. - * - * @param row The row to set the cursor. - * @return true whenever this method does not throw an exception. - * @throws DriverException If an IOException occurs. - */ - public boolean absolute(int row) throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - try // juliana@114_10: if the table of the result set has deleted rows, the absolute row must be searched. - { - byte[] rowsBitmap = allRowsBitmap; - Table tableAux = table; - PlainDB plainDB = tableAux.db; - DataStreamLB basds = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - ByteArrayStream bas = plainDB.bas; - int last = lastRecordIndex; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap != null) - { - int rowCount = 0; - - while (rowCount <= last && rowCount <= row) - { - if ((rowsBitmap[rowCount >> 3] & (1 << (rowCount & 7))) == 0) - row++; - rowCount++; - } - - plainDB.read(pos = rowCount - 1); - bas.setPos(0); // Resets the position of the buffer read. - tableAux.readNullBytesOfRecord(0, false, 0); - } - else if (tableAux.deletedRowsCount > 0) - { - int rowCount = 0; - - // Continues searching the position until finding the right row or the end of the result set table. - while (rowCount <= last && rowCount <= row) // juliana@211_4: solved bugs with result set dealing. - { - plainDB.read(rowCount++); // Reads the next row. - - // If it was deleted, one more row will be read in total. - if ((basds.readInt() & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED) - row++; - } - pos = rowCount - 1; - - bas.setPos(0); // Resets the position of the buffer read. - tableAux.readNullBytesOfRecord(0, false, 0); - } - - // guich@300: removed lastRecordIndex + 1. If there are no deleted rows, just reads the row in the right position. - else if (0 <= row && row <= last) - { - plainDB.read(pos = row); - tableAux.readNullBytesOfRecord(0, false, 0); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - return true; - } - - /** - * Moves the cursor rows in distance. The value can be greater or lower than zero. - * - * @param rows The distance to move the cursor. - * @return true whenever this method does not throw an exception. - * @throws DriverException If an IOException occurs. - */ - public boolean relative(int rows) throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - try // juliana@114_10: if the table of the result set has deleted rows, the relative row must be searched. - { - byte[] rowsBitmap = allRowsBitmap; - Table tableAux = table; - PlainDB plainDB = tableAux.db; - ByteArrayStream bas = plainDB.bas; - int last = lastRecordIndex, - rowCount = pos; - - if (rowsBitmap != null) - { - // Continues searching the position until finding the right row or the end or the beginning of the result set table. - if (rows > 0) - while (--rows >= 0) - while (rowCount++ < last && (rowsBitmap[rowCount >> 3] & (1 << (rowCount & 7))) == 0); - else - while (++rows <= 0) - while (rowCount-- > 0 && (rowsBitmap[rowCount >> 3] & (1 << (rowCount & 7))) == 0); - - if (rowCount < 0) - while (rowCount++ < last && (rowsBitmap[rowCount >> 3] & (1 << (rowCount & 7))) == 0); - if (rowCount > last) - while (rowCount-- > 0 && (rowsBitmap[rowCount >> 3] & (1 << (rowCount & 7))) == 0); - - plainDB.read(pos = rowCount); - tableAux.readNullBytesOfRecord(0, false, 0); - } - else if (tableAux.deletedRowsCount > 0) - { - DataStreamLB basds = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - - // Continues searching the position until finding the right row or the end or the beginning of the result set table. - if (rows > 0) - while (--rows >= 0) - while (rowCount++ < last) - { - plainDB.read(rowCount); - if ((basds.readInt() & Utils.ROW_ATTR_MASK) != Utils.ROW_ATTR_DELETED) - break; - } - else - while (++rows <= 0) - while (rowCount-- > 0) - { - plainDB.read(rowCount); - if ((basds.readInt() & Utils.ROW_ATTR_MASK) != Utils.ROW_ATTR_DELETED) - break; - } - if (rowCount < 0) - while (rowCount++ < last) - { - plainDB.read(rowCount); - if ((basds.readInt() & Utils.ROW_ATTR_MASK) != Utils.ROW_ATTR_DELETED) - break; - } - if (rowCount > last) - while (rowCount-- > 0) - { - plainDB.read(rowCount); - if ((basds.readInt() & Utils.ROW_ATTR_MASK) != Utils.ROW_ATTR_DELETED) - break; - } - - pos = rowCount; - bas.setPos(0); // Resets the position of the buffer read. - tableAux.readNullBytesOfRecord(0, false, 0); - } - else - { - // The new pos is pos + rows or 0 (if pos + rows < 0) or bag.lastRecordIndex (if pos + rows > bag.lastRecordIndex). - int newPos = Math.max(0, Math.min(last, pos + rows)); - - if (pos != newPos) // If there are no deleted rows, just reads the row in the right position. - { - plainDB.read(pos = newPos); - tableAux.readNullBytesOfRecord(0, false, 0); - } - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - return true; - } - - // juliana@265_1: corrected getRow() behavior, which must match with absolute(). - /** - * Returns the current physical row of the table where the cursor is. It must be used with absolute() method. - * - * @return The current physical row of the table where the cursor is. - */ - public int getRow() - { - verifyResultSet(); // The driver or result set can't be closed. - - if (pos == -1 || pos == lastRecordIndex) - return pos; - - try - { - byte[] rowsBitmap = allRowsBitmap; - Table tableAux = table; - PlainDB plainDB = tableAux.db; - DataStreamLE basds = plainDB.basds; - ByteArrayStream bas = plainDB.bas; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap != null) - { - int i = -1, - absolute = 0; - - while (++i < pos) - if ((rowsBitmap[i >> 3] & (1 << (i & 7))) != 0) - absolute++; - return absolute; - } - - // juliana@114_10: if it is a simple select, there may be deleted rows, which must be skiped. - if (tableAux.deletedRowsCount > 0) - { - int i = -1, - absolute = 0; - - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (++i < pos) - { - plainDB.read(i); - if (!((basds.readInt() & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED)) - absolute++; - } - - plainDB.read(i - 1); - bas.setPos(0); - tableAux.readNullBytesOfRecord(0, false, 0); - return absolute; - } - - return pos; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Sets the number of decimal places that the given column (starting from 1) will have when being converted to String. - * - * @param col The column. - * @param places The number of decimal places. - * @throws DriverException If the value for decimal places is invalid or the column is not of type float or double. - */ - public void setDecimalPlaces(int col, int places) throws DriverException - { - checkColumn(col); // Checks the column index, the result set, and driver state. - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@114_10: skips the rowid. - - if (allRowsBitmap != null || isSimpleSelect) - { - SQLResultSetField field = fields[col - 1]; - col = field.parameter == null? field.tableColIndex + 1 : field.parameter.tableColIndex + 1; - } - - int type = table.columnTypes[--col]; // Gets the column type. - - if (places < -1 || places > 40) // Invalid value for decimal places. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RS_DEC_PLACES_START) + places - + LitebaseMessage.getMessage(LitebaseMessage.ERR_RS_DEC_PLACES_END)); - - if (type == SQLElement.FLOAT || type == SQLElement.DOUBLE) // Only sets the decimal places if the type is FLOAT or DOUBLE. - { - byte[] decimals = decimalPlaces; - - if (decimals == null) - Convert.fill(decimals = decimalPlaces = new byte[columnCount], 0, columnCount, -1); - decimals[col] = (byte)places; - } - else - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - } - - /** - * Returns the number of rows of the result set. - * - * @return The number of rows. - */ - public int getRowCount() - { - verifyResultSet(); // The driver or result set can't be closed. - return allRowsBitmap == null? lastRecordIndex + 1 - table.deletedRowsCount : answerCount; // juliana@114_10: Removes the deleted rows. - } - - /** - * Given the column index (starting from 1), indicates if this column has a NULL. - * - * @param colIdx The column index. - * @return true if the value is SQL NULL; false, otherwise. - */ - public boolean isNull(int colIdx) - { - checkColumn(colIdx); // Checks the column index, the result set, and driver state. - return privateIsNull(colIdx); // Is the column null? - } - - /** - * Given the column name (case insensitive), indicates if this column has a NULL. - * - * @param colName The column name. - * @return true if the value is SQL NULL; false, otherwise. - * @throws DriverException If the column name is not found. - */ - public boolean isNull(String colName) throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - int col = htName2index.get(colName.toLowerCase().hashCode(), -1); // Gets the column. - - if (col == -1) // Tests if the column name is mapped in the result set. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND) + colName); - return privateIsNull(col + 1); // Is the column null? - } - - // juliana@270_30: added ResultSet.rowToString(). - /** - * Transforms a ResultSet row in a string. - * - * @return Returns a whole current row of a ResultSet in a string with column data separated by tab. With a column has a - * null or empty value, the string will have two consecutive tabs "\t\t". Blobs are treated as nulls. - * @throws DriverException If the ResultSet position is invalid or an IOException occurs. - */ - public String rowToString() throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - if (pos < 0 || pos > lastRecordIndex) // The position of the cursor must be greater then 0 and less then the last position. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RS_INV_POS)); - - Table tableAux = table; - boolean isTemporary = (allRowsBitmap == null && !isSimpleSelect); - short[] offsets = tableAux.columnOffsets; - byte[] types = tableAux.columnTypes; - byte[] decimals = decimalPlaces; - byte[] nulls = tableAux.columnNulls[0]; - SQLResultSetField[] rsFields = fields; - SQLResultSetField field; - SQLValue value = vrs; - int columns = rsFields.length, - column, - i = -1; - StringBuffer sBuffer = driver.sBuffer; - - sBuffer.setLength(0); - - while (++i < columns) - { - field = rsFields[i]; - if (isTemporary) - column = i; - else - column = field.parameter == null? field.tableColIndex : field.parameter.tableColIndex; - - try - { - // Only reads the column if it is not null and not a BLOB. - if ((nulls[column >> 3] & (1 << (column & 7))) == 0 && types[column] != SQLElement.BLOB) - { - // juliana@220_3 - tableAux.readValue(value, offsets[column], types[column], false, false); - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - if (field.isDataTypeFunction) - applyDataTypeFunction(field, SQLElement.UNDEFINED); - else - createString(types[column], decimals == null? - 1: decimals[column]); - - sBuffer.append(value.asString).append('\t'); - } - else - sBuffer.append('\t'); - } - catch (InvalidDateException exception) {} // Never occurs. - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - sBuffer.setLength(sBuffer.length() - 1); - return sBuffer.toString(); - } - - /** - * Returns a column value of the result set given its type and column index. DATE and DATETIME values will be returned as a single int or as a - * short and an int, respectivelly. - * - * @param colIdx The column index. - * @param type The type of the column. SQLElement.UNDEFINED must be used to return anything except for blobs as strings. - * @return true if the column is not null; false otherwise. - */ - private boolean getFromIndex(int colIdx, int type) - { - checkColumn(colIdx); // Checks the column index, the result set, and driver state. - return privateGetFromIndex(colIdx, type); - } - - /** - * Returns a column value of the result set given its type and column name. DATE and DATETIME values will be returned as a single int or as a - * short and an int, respectivelly. - * - * @param colIdx The column name. - * @param type The type of the column. SQLElement.UNDEFINED must be used to return anything except for blobs as strings. - * @return true if the column is not null; false otherwise. - * @throws DriverException If the column name is not found. - */ - private boolean getFromName(String colName, int type) throws DriverException - { - verifyResultSet(); // The driver or result set can't be closed. - - int col = htName2index.get(colName.toLowerCase().hashCode(), -1); // Gets the column index. - // juliana@227_14: corrected a DriverException not being thrown when fetching in some cases when trying to fetch data from an invalid result - // set column. - - if (col == -1) // Tests if the column name is mapped in the result set. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND) + colName); - - return privateGetFromIndex(col + 1, type); - } - - /** - * Gets the value of a column of the underlying table used by the result set. - * - * @param col The number of the column from which the value will be fetched. - * @param val The object where the value will be stored. - * @return The value read. - * @throws IOExeption If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - SQLValue sqlwhereclausetreeGetTableColValue(int col, SQLValue val) throws IOException, InvalidDateException - { - Table tableAux = table; - - // juliana@220_3 - table.readValue(val, tableAux.columnOffsets[col], tableAux.columnTypes[col], (tableAux.columnNulls[0][col >> 3] & (1 << (col & 7))) != 0, - true); - return val; - } - - /** - * Gets the next record of a result set. This method is to be used by the result sets created internally by the Litebase code, not by external - * result sets. - * - * @return true if there is a next record to go to in the result set; false, otherwise. - * @throws IOExeption If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - boolean getNextRecord() throws IOException, InvalidDateException, InvalidNumberException - { - PlainDB db = table.db; - IntVector rowsBitmapAux = rowsBitmap; - SQLBooleanClause clause = whereClause; - int last = lastRecordIndex; - - if (rowsBitmapAux != null) // Desired rows partially computed using the indexes? - { - int p; - int[] items = rowsBitmapAux.items; - if (pos < last) - { - if (clause == null) - { - // juliana@227_7: solved a bug on delete when trying to delete a key from a column which has index and there are deleted rows with the - // same key. - // No WHERE clause. Just returns the rows marked in the bitmap. - while ((p = Utils.findNextBitSet(items, pos + 1)) != -1 && p <= last) - { - db.read(pos = p); - if (db.recordNotDeleted()) - return true; - } - } - else - // With a remaining WHERE clause there are 2 situations. - // 1) The relationship between the bitmap and the WHERE clause is an AND relationship, and - // 2) The relationship between the bitmap and the WHERE clause is an OR relationship. - if (rowsBitmapBoolOp == SQLElement.OP_BOOLEAN_AND) - // AND case - walks through the bits that are set in the bitmap and checks if rows satisfies the where clause. - while ((p = Utils.findNextBitSet(items, pos + 1)) != -1 && p <= last) - { - db.read(pos = p); - - // juliana@227_7: solved a bug on delete when trying to delete a key from a column which has index and there are deleted rows - // with the same key. - if (db.recordNotDeleted() && clause.sqlBooleanClauseSatisfied(this)) - return true; - } - else - // OR case - walks through all records. If the corresponding bit is set in the bitmap, do not need to evaluate WHERE clause. - // Otherwise, checks if the row satisifies the WHERE clause. - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (pos++ < last) - { - db.read(pos); - if (rowsBitmapAux.isBitSet(pos) || (db.recordNotDeleted() && clause.sqlBooleanClauseSatisfied(this))) - return true; - } - - } - return false; - } - else - // If the where clause exists, it needs to be satisfied. - while (pos++ < last) // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - { - db.read(pos); - if (db.recordNotDeleted() && (clause == null || clause.sqlBooleanClauseSatisfied(this))) - return true; - } - - return false; - } - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - /** - * Applies a function when fetching data from the result set. - * - * @param field The field where the function is being applied. - * @param type The type of the field being returned. - */ - private void applyDataTypeFunction(SQLResultSetField field, int type) - { - vrs.applyDataTypeFunction(field.sqlFunction, field.parameter.dataType); - if (type == SQLElement.UNDEFINED) - switch (field.sqlFunction) - { - case SQLElement.FUNCTION_DT_YEAR: - case SQLElement.FUNCTION_DT_MONTH: - case SQLElement.FUNCTION_DT_DAY: - case SQLElement.FUNCTION_DT_HOUR: - case SQLElement.FUNCTION_DT_MINUTE: - case SQLElement.FUNCTION_DT_SECOND: - case SQLElement.FUNCTION_DT_MILLIS: - vrs.asString = Convert.toString(vrs.asShort); - break; - case SQLElement.FUNCTION_DT_ABS: - switch (field.parameter.dataType) - { - case SQLElement.SHORT: - vrs.asString = Convert.toString(vrs.asShort); - break; - case SQLElement.INT: - vrs.asString = Convert.toString(vrs.asInt); - break; - case SQLElement.LONG: - vrs.asString = Convert.toString(vrs.asLong); - break; - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - vrs.asString = Convert.toString(vrs.asDouble); - } - } - } - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - /** - * Creates a string to return to the user. - * - * @param type The type of the value being returned to the user. - * @param decimalPlaces The number of decimal places if the value is a floating point number. - * @throws InvalidDateException Never occurs. - */ - private void createString(int type, int decimalPlaces) throws InvalidDateException - { - switch (type) - { - case SQLElement.SHORT: - vrs.asString = Convert.toString(vrs.asShort); - break; - case SQLElement.INT: - vrs.asString = Convert.toString(vrs.asInt); - break; - case SQLElement.LONG: - vrs.asString = Convert.toString(vrs.asLong); - break; - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - vrs.asString = Convert.toString(vrs.asDouble, decimalPlaces); - break; - case SQLElement.DATE: - int date = vrs.asInt; - driver.tempDate.set(date % 100, (date /= 100) % 100, date / 100); - vrs.asString = driver.tempDate.toString(); - break; - case SQLElement.DATETIME: - StringBuffer buffer = driver.sBuffer; - - buffer.setLength(0); - date = vrs.asInt; - driver.tempDate.set(date % 100, (date /= 100) % 100, date / 100); - buffer.append(driver.tempDate).append(' '); - Utils.formatTime(buffer, vrs.asShort); - vrs.asString = buffer.toString(); - } - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Verifies if the result set and its driver are opened. - * - * @throws IllegalStateException If the result set or its driver is closed. - */ - void verifyResultSet() throws IllegalStateException - { - // juliana@211_4: solved bugs with result set dealing. - if (table == null) // The ResultSet can't be closed. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RESULTSET_CLOSED)); - if (driver.htTables == null) // juliana@227_4: the connection where the result set was created can't be closed while using it. - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Verifies if the result set and its driver are opened and checks the column index. - * - * @param column The column to be checked. - * @throws IllegalArgumentException If the parameter is out of bounds. - */ - private void checkColumn(int column) - { - verifyResultSet(); // The driver or result set can't be closed. - - // juliana@227_14: corrected a DriverException not being thrown when fetching in some cases when trying to fetch data from an invalid result - // set column. - if (column <= 0 || column > columnCount) // The columns given by the user ranges from 1 to n. - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + column); - } - - /** - * Indicates if this column has a NULL. - * - * @param column The column index. - * @return true if the value is SQL NULL; false, otherwise. - */ - private boolean privateIsNull(int column) - { - // juliana@114_10: skips the rowid. - - if (allRowsBitmap != null || isSimpleSelect) - { - SQLResultSetField field = fields[column - 1]; - column = field.parameter == null? field.tableColIndex + 1 : field.parameter.tableColIndex + 1; - } - - return (table.columnNulls[0][column - 1 >> 3] & (1 << (column - 1 & 7))) != 0; // Is the column null? - } - - /** - * Returns a column value of the result set given its type and column index. DATE and DATETIME values will be returned as a single int or as a - * short and an int, respectivelly. - * - * @param column The column index. - * @param type The type of the column. SQLElement.UNDEFINED must be used to return anything except for blobs as strings. - * @return true if the column is not null; false otherwise. - * @throws DriverException If an IOExceptionoccurs or the kind of return type asked is incompatible from the column definition type. - */ - private boolean privateGetFromIndex(int column, int type) - { - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - SQLResultSetField field = fields[column - 1]; - - // juliana@114_10: skips the rowid. - - if (allRowsBitmap != null || isSimpleSelect) - column = field.parameter == null? field.tableColIndex + 1 : field.parameter.tableColIndex + 1; - - // juliana@201_23: the types must be compatible. - // juliana@227_13: corrected a DriverException not being thrown when issuing ResultSet.getChars() for a column that is not of CHARS, CHARS - // NOCASE, VARCHAR, or VARCHAR NOCASE. - int typeCol = table.columnTypes[column - 1]; - - // juliana@270_28: now it is not allowed to fetch a string field in ResultSet with methods that aren't getString() or getChars(). - if (type != SQLElement.UNDEFINED) - if (!(field.isDataTypeFunction && type == SQLElement.SHORT && (typeCol == SQLElement.DATE || typeCol == SQLElement.DATETIME)) - && (typeCol != type - && ((typeCol != SQLElement.CHARS_NOCASE && typeCol != SQLElement.CHARS) || (type != SQLElement.CHARS_NOCASE && type != SQLElement.CHARS)))) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - if (type == SQLElement.UNDEFINED && typeCol == SQLElement.BLOB) // getString() returns null for blobs. - vrs.asString = null; - - if ((table.columnNulls[0][column - 1 >> 3] & (1 << (column - 1 & 7))) == 0) // Only reads the column if it is not null. - { - if (pos < 0 || pos > lastRecordIndex) // The position of the cursor must be greater then 0 and less then the last position. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_RS_INV_POS)); - - try // Reads and returns the value read. - { - table.readValue(vrs, table.columnOffsets[--column], typeCol, false, false); // juliana@220_3 - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - if (field.isDataTypeFunction) - applyDataTypeFunction(field, type); - else if (type == SQLElement.UNDEFINED) - createString(typeCol, decimalPlaces == null? - 1: decimalPlaces[column]); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - return true; - } - - return false; - } -} diff --git a/LitebaseSDK/src/java/litebase/ResultSet4D.java b/LitebaseSDK/src/java/litebase/ResultSet4D.java deleted file mode 100644 index ac3d73ce2a..0000000000 --- a/LitebaseSDK/src/java/litebase/ResultSet4D.java +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.*; -import totalcross.util.*; - -/** - * This native class represents a set or rows resulting from a LitebaseConnection.executeQuery() method call. Here's an example: - * - *
    - * ResultSet rs = driver.executeQuery("select name, salary, age from person");
    - * while (rs.next())
    - * {
    - *    Vm.debug(pad(rs.getString("name"), 32) + pad(rs.getString("salary"), 16) + rs.getInt("age") + " years");
    - * }
    - * 
    - * - * Result sets cannot be constructed directly; instead, an sql must be issued to the driver. - */ -@Deprecated -public class ResultSet4D -{ - /** - * The ResultSetBag object, which stores the ResultSet data. - */ - long bag; - - /** - * A flag that indicates that this class has already been finalized. - */ - boolean dontFinalize; - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - private ResultSet4D() {} - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the metadata for this result set. - * - * @return The metadata for this result set. - */ - public native ResultSetMetaData4D getResultSetMetaData(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Releases all memory allocated for this object. Its a good idea to call this when you no longer needs it, but it is also called by the GC when - * the object is no longer in use. - * - * @throws IllegalStateException If the result set is closed. - */ - public native void close() throws IllegalStateException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Places the cursor before the first record. - */ - public native void beforeFirst(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Places the cursor after the last record. - */ - public native void afterLast(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Places the cursor in the first record of the result set. - * - * @return true if it was possible to place the cursor in the first record; false, otherwise. - */ - public native boolean first(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Places the cursor in the last record of the result set. - * - * @return true if it was possible to place the cursor in the last record; false, otherwise. - */ - public native boolean last(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Gets the next record of the result set. - * - * @return true if there is a next record to go to in the result set; false, otherwise. - */ - public native boolean next(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the previous record of the result set. - * - * @return true if there is a previous record to go to in the result set; false, otherwise. - */ - public native boolean prev(); - - /** - * Given the column index (starting from 1), returns a short value that is represented by this column. Note that it is only possible to request - * this column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public native short getShort(int colIdx); - - /** - * Given the column name (case insensitive), returns a short value that is represented by this column. Note that it is only possible to request - * this column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. This - * method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public native short getShort(String colName); - - /** - * Given the column index (starting from 1), returns an integer value that is represented by this column. Note that it is only possible to request - * this column as integer if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public native int getInt(int colIdx); - - /** - * Given the column name (case insensitive), returns an integer value that is represented by this column. Note that it is only possible to request - * this column as integer if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public native int getInt(String colName); - - /** - * Given the column index (starting from 1), returns a long value that is represented by this column. Note that it is only possible to request - * this column as long if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public native long getLong(int colIdx); - - /** - * Given the column name (case insensitive), returns a long value that is represented by this column. Note that it is only possible to request - * this column as long if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ - public native long getLong(String colName); - - /** - * Given the column index (starting from 1), returns a float value that is represented by this column. Note that it is only possible to request - * this column as float if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public native double getFloat(int colIdx); - - /** - * Given the column name (case insensitive), returns a float value that is represented by this column. Note that it is only possible to request - * this column as float if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public native double getFloat(String colName); - - /** - * Given the column index (starting from 1), returns a double value that is represented by this column. Note that it is only possible to request - * this column as double if it was created with this precision. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public native double getDouble(int colIdx); - - /** - * Given the column name (case insensitive), returns a double value that is represented by this column. Note that it is only possible to request - * this column as double if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ - public native double getDouble(String colName); - - /** - * Given the column index (starting from 1), returns a char array that is represented by this column. Note that it is only possible to request - * this column as a char array if it was created as a string. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public native char[] getChars(int colIdx); - - /** - * Given the column name (case insensitive), returns a char array that is represented by this column. Note that it is only possible to request - * this column as a char array if it was created as a string. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public native char[] getChars(String colName); - - /** - * Given the column index (starting from 1), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is null - */ - public native String getString(int colIdx); - - /** - * Given the column name (case insensitive), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. This - * method is slightly slower then the method that accepts a column index. - * - * @param colName The column index. - * @return The column value; if the value is SQL NULL, the value returned is null - */ - public native String getString(String colName); - - /** - * Given the column index (starting from 1), returns a byte (blob) array that is represented by this column. Note that it is only possible to - * request this column as a blob if it was created this way. - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public native byte[] getBlob(int colIdx); - - /** - * Given the column name (case insensitive), returns a byte array (blob) that is represented by this column. Note that it is only possible to - * request this column as a blob if it was created this way. This method is slightly slower then the method that accepts a column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public native byte[] getBlob(String colName); - - /** - * Starting from the current cursor position, it reads all result set rows that are being requested. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param count The number of rows to be fetched, or -1 for all. - * @return A matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column elements of the - * first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is - * null. - */ - public native String[][] getStrings(int count); - - /** - * Starting from the current cursor position, it reads all result set rows of the result set. first(), last(), - * prev() or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @return A matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column elements of the - * first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is - * null. - */ - public native String[][] getStrings(); - - /** - * Given the column index (starting from 1), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE or DATETIME). - * - * @param colIdx The column index. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public native Date getDate(int colIdx); // rnovais@567_3 - - /** - * Given the column name (case insensitive), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE or DATETIME). This method is slightly slower then the method that accepts a - * column index. - * - * @param colName The column name. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ - public native Date getDate(String colName); // rnovais@567_3 - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Given the column index (starting from 1), returns a Time (correspondent to a DATETIME data type) value that is represented by - * this column. Note that it is only possible to request this column as a date if it was created this way. - * - * @param colIdx The colum index. - * @return The time of the DATETIME. If the DATETIME value is SQL NULL, the value returned is null. - */ - public native Time getDateTime(int colIdx); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Given the column name (case insensitive), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a date if it was created this way. This method is slightly slower then the - * method that accepts a column index. - * - * @param colName The colum name. - * @return The time of the DATETIME. If the DATETIME value is SQL NULL, the value returned is null. - */ - public native Time getDateTime(String colName); // rnovais@567_3 - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Places this result set cursor at the given absolute row. This is the absolute physical row of the table. This method is usually used to restore - * the row at a previous row got with the getRow() method. - * - * @param row The row to set the cursor. - * @return true whenever this method does not throw an exception. - */ - public native boolean absolute(int row); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Moves the cursor rows in distance. The value can be greater or lower than zero. - * - * @param rows The distance to move the cursor. - * @return true whenever this method does not throw an exception. - */ - public native boolean relative(int rows); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the current physical row of the table where the cursor is. It must be used with absolute() method. - * - * @return The current physical row of the table where the cursor is. - */ - public native int getRow(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Sets the number of decimal places that the given column (starting from 1) will have when being converted to String. - * - * @param col The column. - * @param places The number of decimal places. - * @throws DriverException If the column index is invalid, or the value for decimal places is invalid. - */ - public native void setDecimalPlaces(int col, int places) throws DriverException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Returns the number of rows of the result set. - * - * @return The number of rows. - */ - public native int getRowCount(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Given the column index (starting from 1), indicates if this column has a NULL. - * - * @param col The column index. - * @return true if the value is SQL NULL; false, otherwise. - */ - public native boolean isNull(int col); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Given the column name (case insensitive), indicates if this column has a NULL. - * - * @param colName The column name. - * @return true if the value is SQL NULL; false, otherwise. - * @throws NullPointerException If the column name is null. - */ - public native boolean isNull(String colName) throws NullPointerException; - - // juliana@270_30: added ResultSet.rowToString(). - /** - * Transforms a ResultSet row in a string. - * - * @return Returns a whole current row of a ResultSet in a string with column data separated by tab. - * @throws DriverException If the ResultSet position is invalid. - */ - public native String rowToString() throws DriverException; - - /** - * Finalizes the ResultSet object. - */ - protected void finalize() - { - close(); - } -} diff --git a/LitebaseSDK/src/java/litebase/ResultSetMetaData.java b/LitebaseSDK/src/java/litebase/ResultSetMetaData.java deleted file mode 100644 index 416614b045..0000000000 --- a/LitebaseSDK/src/java/litebase/ResultSetMetaData.java +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.IOException; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * This class returns useful information for the ResultSet columns. Note that the information can be retrieved even if the - * ResultSet returns no data. - *

    Important: it is not possible to retrieve these information if the ResultSet is closed! - */ -@Deprecated -public class ResultSetMetaData -{ - /** - * Used by the getColumnType method to indicate that the column is of type CHAR. - */ - public static final int CHAR_TYPE = 0; - - /** - * Used by the getColumnType method to indicate that the column is of type SHORT. - */ - public static final int SHORT_TYPE = 1; - - /** - * Used by the getColumnType method to indicate that the column is of type INT. - */ - public static final int INT_TYPE = 2; - - /** - * Used by the getColumnType method to indicate that the column is of type LONG. - */ - public static final int LONG_TYPE = 3; - - /** - * Used by the getColumnType method to indicate that the column is of type FLOAT. - */ - public static final int FLOAT_TYPE = 4; - - /** - * Used by the getColumnType method to indicate that the column is of type DOUBLE. - */ - public static final int DOUBLE_TYPE = 5; - - /** - * Used by the getColumnType method to indicate that the column is of type CHARS_NOCASE. - */ - public static final int CHAR_NOCASE_TYPE = 6; - - // The type BOOLEAN_TYPE is absent because a column will never have this type. - // This is only used with expressions and this type would have the value 7. - - /** - * Used by the getColumnType method to indicate that the column is of type DATE_TYPE. - */ - public static final int DATE_TYPE = 8; - - /** - * Used by the getColumnType method to indicate that the column is of type DATETIME_TYPE. - */ - public static final int DATETIME_TYPE = 9; - - /** - * Used by the getColumnType method to indicate that the column is of type BLOB_TYPE. - */ - public static final int BLOB_TYPE = 10; - - /** - * The underlying ResultSet. - */ - private ResultSet rs; - - /** - * Constructs a new ResultSetMetaData for a given ResultSet. - * - * @param aRs The ResultSet. - */ - ResultSetMetaData(ResultSet aRs) - { - rs = aRs; - } - - /** - * Gets the number of columns for this ResultSet. - * - * @return The number of columns for this ResultSet. - */ - public int getColumnCount() - { - rs.verifyResultSet(); // The driver or result set can't be closed. - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not in - // the select clause. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@114_10: skips the rowid. - return rs.fields.length; - } - - /** - * Given the column index (starting at 1), returns the display size. For chars, it will return the number of chars defined; for primitive types, - * it will return the number of decimal places it needs to be displayed correctly. Returns 0 if an error occurs. - * - * @param column The column index (starting at 1). - * @return The display size or -1 if a problem occurs. - */ - public int getColumnDisplaySize(int column) - { - ResultSet resultSet = rs; - verifyRSMDState(column); - - // juliana@114_10: skips the rowid. - - if (resultSet.allRowsBitmap != null || resultSet.isSimpleSelect) - { - SQLResultSetField field = resultSet.fields[column - 1]; - column = field.parameter == null? field.tableColIndex + 1: field.parameter.tableColIndex + 1; - } - - switch (resultSet.table.columnTypes[column - 1]) - { - case ResultSetMetaData.SHORT_TYPE: - return 6; // guich@560_4: adjusted the sizes - case ResultSetMetaData.INT_TYPE: - return 11; - case ResultSetMetaData.LONG_TYPE: - return 20; - case ResultSetMetaData.FLOAT_TYPE: - return 13; - case ResultSetMetaData.DOUBLE_TYPE: - return 21; - case ResultSetMetaData.CHAR_TYPE: - case ResultSetMetaData.CHAR_NOCASE_TYPE: - return resultSet.table.columnSizes[column - 1]; - case ResultSetMetaData.DATE_TYPE: - return 11; // rnovais@570_12 - case ResultSetMetaData.DATETIME_TYPE: - return 31; // (11 + 20) // rnovais@570_12 - // BLOBs can't be displayed. - } - return -1; - - } - - /** - * Given the column index (starting at 1), returns the column name. Note that if an alias is used to the column, the alias will be returned - * instead. If an error occurs, an empty string is returned. Note that LitebaseConnection 2.x tables must be recreated to be able to return this label information. - * - * @param column The column index (starting at 1). - * @return The name or alias of the column, which can be an empty string if an error occurs. - */ - public String getColumnLabel(int column) - { - verifyRSMDState(column); - return rs.fields[column - 1].alias; - } - - /** - * Given the column index (starting at 1), returns the column type. - * - * @param column The column index (starting at 1). - * @return The column type, which can be: SHORT_TYPE, INT_TYPE, LONG_TYPE, - * FLOAT_TYPE, DOUBLE_TYPE, CHAR_TYPE, CHAR_NOCASE_TYPE, - * DATE_TYPE, DATETIME_TYPE, or BLOB_TYPE. - */ - public int getColumnType(int column) - { - ResultSet resultSet = rs; - - verifyRSMDState(column); - if (resultSet.allRowsBitmap != null || resultSet.isSimpleSelect) - { - SQLResultSetField field = resultSet.fields[column - 1]; - return resultSet.table.columnTypes[field.parameter == null? field.tableColIndex : field.parameter.tableColIndex]; - } - return resultSet.table.columnTypes[column - 1]; - - // juliana@114_10: skips the rowid. - } - - /** - * Given the column index (starting at 1), returns the name of the column type. - * - * @param column The column index (starting at 1). - * @return The name of the column type, which can be: chars, short, int, - * long, float, double, date, datetime, - * blob, or null if an error occurs. - */ - public String getColumnTypeName(int column) - { - switch (getColumnType(column)) // Gets the string representation of the type. - { - case CHAR_TYPE: - case CHAR_NOCASE_TYPE: - return "chars"; - case SHORT_TYPE: - return "short"; - case INT_TYPE: - return "int"; - case LONG_TYPE: - return "long"; - case FLOAT_TYPE: - return "float"; - case DOUBLE_TYPE: - return "double"; - case DATE_TYPE: - return "date"; - case DATETIME_TYPE: - return "datetime"; - case BLOB_TYPE: - return "blob"; - } - return null; - } - - /** - * Given the column index, (starting at 1) returns the name of the table it came from. - * - * @param columnIdx The column index. - * @return The name of the table it came from. - */ - public String getColumnTableName(int columnIdx) - { - verifyRSMDState(columnIdx); - return rs.fields[columnIdx - 1].tableName; - } - - /** - * Given the column name or alias, returns the name of the table it came from. - * - * @param columnName The column name. - * @return The name of the table it came from or null if the column name does not exist. - * @throws DriverException If the column was not found. - */ - public String getColumnTableName(String columnName) throws DriverException - { - rs.verifyResultSet(); // The driver or result set can't be closed. - - SQLResultSetField[] fields = rs.fields; - int i = -1, - len = fields.length; - - while (++i < len) // Gets the name of the table or its alias given the column name. - if (columnName.equalsIgnoreCase(fields[i].tableColName) || columnName.equalsIgnoreCase(fields[i].alias)) - return fields[i].tableName; - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND) + columnName); - } - - // juliana@227_2: added methods to indicate if a column of a result set is not null or has default values. - /** - * Indicates if a column of the result set has default value. - * - * @param columnIndex The column index. - * @return true if the column has a default value; false, otherwise. - * @throws DriverException If an IOException occurs or the column index does not have an underlining table. - */ - public boolean hasDefaultValue(int columnIndex) throws DriverException - { - try // Gets the table column info. - { - String name = getColumnTableName(columnIndex); // It already tests if the result set is valid. - SQLResultSetField field = rs.fields[columnIndex - 1]; - - if (name != null) - return ((rs.driver.getTable(name)).columnAttrs[field.tableColIndex >= 0? field.tableColIndex - : field.parameter.tableColIndex] & Utils.ATTR_COLUMN_HAS_DEFAULT) != 0; - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + columnIndex); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return false; - } - } - - /** - * Indicates if a column of the result set has default value. - * - * @param columnName The column name. - * @return true if the column has a default value; false, otherwise. - * @throws DriverException If the column was not found, does not have an underlining table, or an IOException occurs. - */ - public boolean hasDefaultValue(String columnName) throws DriverException - { - ResultSet resultSet = rs; - - resultSet.verifyResultSet(); // The driver or result set can't be closed. - - SQLResultSetField[] fields = resultSet.fields; - SQLResultSetField field; - int i = -1, - len = fields.length; - - while (++i < len) // Gets the name of the table or its alias given the column name and gets the table column info. - { - if (columnName.equalsIgnoreCase((field = fields[i]).tableColName) || columnName.equalsIgnoreCase(fields[i].alias)) - try - { - if (field.tableName != null) - return ((resultSet.driver.getTable(field.tableName).columnAttrs[field.tableColIndex >= 0? field.tableColIndex - : field.parameter.tableColIndex]) & Utils.ATTR_COLUMN_HAS_DEFAULT) != 0; - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + columnName); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - } - if (i == len) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND) + columnName); - - return false; - } - - /** - * Indicates if a column of the result set is not null. - * - * @param columnIndex The column index. - * @return true if the column is not null; false, otherwise. - * @throws DriverException If an IOException occurs or the column index does not have an underlining table. - */ - public boolean isNotNull(int columnIndex) throws DriverException - { - try // Gets the table column info. - { - String name = getColumnTableName(columnIndex); // It already tests if the result set is valid. - SQLResultSetField field = rs.fields[columnIndex - 1]; - - if (name != null) - return ((rs.driver.getTable(name)).columnAttrs[field.tableColIndex >= 0? field.tableColIndex - : field.parameter.tableColIndex] & Utils.ATTR_COLUMN_IS_NOT_NULL) != 0; - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + columnIndex); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return false; - } - } - - /** - * Indicates if a column of the result set is not null. - * - * @param columnName The column name. - * @return true if the column is not null; false, otherwise. - * @throws DriverException If the column was not found, does not have an underlining table, or an IOException occurs. - */ - public boolean isNotNull(String columnName) throws DriverException - { - ResultSet resultSet = rs; - - resultSet.verifyResultSet(); // The driver or result set can't be closed. - - SQLResultSetField[] fields = resultSet.fields; - SQLResultSetField field; - int i = -1, - len = fields.length; - - while (++i < len) // Gets the name of the table or its alias given the column name and gets the table column info. - if (columnName.equalsIgnoreCase((field = fields[i]).tableColName) || columnName.equalsIgnoreCase(fields[i].alias)) - try - { - if (field.tableName != null) - return ((resultSet.driver.getTable(field.tableName).columnAttrs[field.tableColIndex >= 0? field.tableColIndex - : field.parameter.tableColIndex]) & Utils.ATTR_COLUMN_IS_NOT_NULL) != 0; - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + columnName); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - - if (i == len) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND) + columnName); - - return false; - } - - // juliana@253_3: added methods to return the primary key columns of a table. - /** - * Returns the primary key column indices of a table. - * - * @param tableName The table name. - * @return null if the given table does not have primary key or an array with the column indices of the primary key. - */ - public byte[] getPKColumnIndices(String tableName) - { - rs.verifyResultSet(); // The driver or result set can't be closed. - - Table table = getTable(tableName); // Gets the table given its name in the result set. - - if (table.primaryKeyCol != Utils.NO_PRIMARY_KEY) // Simple primary key. - return new byte[] {(byte)table.primaryKeyCol}; - if (table.composedPK != Utils.NO_PRIMARY_KEY) // Composed primary key. - { - byte[] composedPK = new byte[table.composedPrimaryKeyCols.length]; - Vm.arrayCopy(table.composedPrimaryKeyCols, 0, composedPK, 0, composedPK.length); - return composedPK; - } - - return null; // The table does not have a primary key. - } - - /** - * Returns the primary key column names of a table. - * - * @param tableName The table name. - * @return null if the given table does not have primary key or an array with the column names of the primary key. - */ - public String[] getPKColumnNames(String tableName) - { - rs.verifyResultSet(); // The driver or result set can't be closed. - - Table table = getTable(tableName); // Gets the table given its name in the result set. - - if (table.primaryKeyCol != Utils.NO_PRIMARY_KEY) // Simple primary key. - return new String[] {table.columnNames[table.primaryKeyCol]}; - if (table.composedPK != Utils.NO_PRIMARY_KEY) // Composed primary key. - { - byte[] composedPKCols = table.composedPrimaryKeyCols; - int i = composedPKCols.length; - String[] composedPKNames = new String[i]; - String[] columnNames = table.columnNames; - - while (--i >= 0) - composedPKNames[i] = columnNames[composedPKCols[i]]; - - return composedPKNames; - } - - return null; // The table does not have a primary key. - } - - // juliana@253_4: added methods to return the default value of a column. - /** - * Returns the default value of a column. - * - * @param columnIndex The column index. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If the column index does not have an underlining table. - */ - public String getDefaultValue(int columnIndex) throws DriverException - { - String name = getColumnTableName(columnIndex); // Already checks if the result set is valid. - ResultSet resultSet = rs; - SQLResultSetField field = resultSet.fields[columnIndex - 1]; - - if (name == null) // The column does not have an underlining table. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + columnIndex); - - // Returns the default value of the column or the parameter of a function. - return getDefault(name, field.tableColIndex >= 0? field.tableColIndex : field.parameter.tableColIndex); - } - - /** - * Returns the default value of a column. - * - * @param columnName The column name. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If the column name does not have an underlining table. - */ - public String getDefaultValue(String columnName) throws DriverException - { - ResultSet resultSet = rs; - - resultSet.verifyResultSet(); // The driver or result set can't be closed. - - SQLResultSetField[] fields = resultSet.fields; - SQLResultSetField field; - int i = -1, - len = fields.length; - - while (++i < len) // Gets the name of the table or its alias given the column name and gets the table column info. - { - if (columnName.equalsIgnoreCase((field = fields[i]).tableColName) || columnName.equalsIgnoreCase(fields[i].alias)) - { - if (field.tableName != null) - return getDefault(field.tableName, field.tableColIndex >= 0? field.tableColIndex : field.parameter.tableColIndex); - - // The column does not have an underlining table. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + columnName); - } - } - if (i == len) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_NOT_FOUND)); - - return null; - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Checks if the driver or the result set is closed, and if the column index is invalid. - * - * @param column The column index. - * @throws IllegalArgumentException If the column index is invalid. - */ - private void verifyRSMDState(int column) throws IllegalArgumentException - { - ResultSet resultSet = rs; - - resultSet.verifyResultSet(); // The driver or result set can't be closed. - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not in - // the select clause. - // juliana@213_5: Now a DriverException is thrown instead of returning an invalid value. - if (column <= 0 || column > resultSet.fields.length) - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + column); - } - - // juliana@253_3: added methods to return the primary key columns of a table. - /** - * Returns a table used in a select given its name. - * - * @param tableName The table name. - * @return The table with the given name. It will never return null. - * @throws DriverException if the given table name is not used in the select or an IOException occurs. - */ - private Table getTable(String tableName) throws DriverException - { - SQLResultSetField[] fields = rs.fields; - int i = fields.length; - - tableName = tableName.toLowerCase(); - - // The table name must be used in the select. - while (--i >= 0 && (fields[i].tableName == null || !fields[i].tableName.equals(tableName))); - if (i == -1) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_NAME_NOT_FOUND) + tableName); - - try - { - return rs.driver.getTable(tableName); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - - return null; - } - - // juliana@253_4: added methods to return the default value of a column. - - /** - * Gets the default value of a column. - * - * @param tableName The name of the table. - * @param index The column index. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If an IOException occurs or the column index is of a column of type BLOB. - */ - private String getDefault(String tableName, int index) throws DriverException - { - try - { - ResultSet resultSet = rs; - Table table = resultSet.driver.getTable(tableName); - int type = table.columnTypes[index]; - SQLValue value = table.defaultValues[index]; - - if (value == null) // No default value, returns null. - return null; - - switch (type) - { - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - return value.asString; - case SQLElement.SHORT: - return Convert.toString(value.asShort); - case SQLElement.INT: - return Convert.toString(value.asInt); - case SQLElement.LONG: - return Convert.toString(value.asLong); - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - return Convert.toString(value.asDouble); - case SQLElement.DATE: - { - int dateInt = value.asInt; - Date dateObj = resultSet.driver.tempDate; - dateObj.set(dateInt % 100, (dateInt /= 100) % 100, dateInt / 100); - return dateObj.toString(); - } - case SQLElement.DATETIME: - { - StringBuffer buffer = resultSet.driver.sBuffer; - - buffer.setLength(0); - Utils.formatDate(buffer, value.asInt); - buffer.append(' '); - Utils.formatTime(buffer, value.asShort); - return buffer.toString(); - } - case SQLElement.BLOB: // Blob can't be used with default. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_STRING)); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) {} - - return null; - } -} diff --git a/LitebaseSDK/src/java/litebase/ResultSetMetaData4D.java b/LitebaseSDK/src/java/litebase/ResultSetMetaData4D.java deleted file mode 100644 index cd00d291b2..0000000000 --- a/LitebaseSDK/src/java/litebase/ResultSetMetaData4D.java +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * This native class returns useful information for the ResultSet columns. Note that the information can be retrieved even if - * ResultSet returns no data. - *

    Important: it is not possible to retrieve these information if the ResultSet is closed! - */ -@Deprecated -public class ResultSetMetaData4D -{ - /** - * Used by the getColumnType method to indicate that the column is of type CHAR. - */ - public static final int CHAR_TYPE = 0; - - /** - * Used by the getColumnType method to indicate that the column is of type SHORT. - */ - public static final int SHORT_TYPE = 1; - - /** - * Used by the getColumnType method to indicate that the column is of type INT. - */ - public static final int INT_TYPE = 2; - - /** - * Used by the getColumnType method to indicate that the column is of type LONG. - */ - public static final int LONG_TYPE = 3; - - /** - * Used by the getColumnType method to indicate that the column is of type FLOAT. - */ - public static final int FLOAT_TYPE = 4; - - /** - * Used by the getColumnType method to indicate that the column is of type DOUBLE. - */ - public static final int DOUBLE_TYPE = 5; - - /** - * Used by the getColumnType method to indicate that the column is of type CHARS_NOCASE. - */ - public static final int CHAR_NOCASE_TYPE = 6; - - // The type BOOLEAN_TYPE is absent because a column will never have this type. This is only used with expressions and this type would have the - // value 7. - /** - * Used by the getColumnType method to indicate that the column is of type DATE_TYPE. - */ - public static final int DATE_TYPE = 8; - - // rnovais@_567_2 - // rnovais@_567_2: stored as two ints. - /** - * Used by the getColumnType method to indicate that the column is of type DATETIME_TYPE. - */ - public static final int DATETIME_TYPE = 9; - - /** - * Used by the getColumnType method to indicate that the column is of type BLOB_TYPE. - */ - public static final int BLOB_TYPE = 10; - - /** - * The underlying ResultSet. - */ - ResultSet4D rs; - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - private ResultSetMetaData4D() {} - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Gets the number of columns for this ResultSet. - * - * @return The number of columns for this ResultSet. - */ - public native int getColumnCount(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Given the column index (starting at 1), returns the display size. For chars, it will return the number of chars defined; for primitive types, - * it will return the number of decimal places it needs to be displayed correctly. Returns 0 if an error occurs. - * - * @param column The column index (starting at 1). - * @return The display size or -1 if a problem occurs. - * @throws IllegalArgumentException If the column index is invalid. - */ - public native int getColumnDisplaySize(int column) throws IllegalArgumentException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Given the column index (starting at 1), returns the column name. Note that if an alias is used to the column, the alias will be returned - * instead. If an error occurs, an empty string is returned. Note that LitebaseConnection 2.x tables must be recreated to be able to return this - * label information. - * - * @param column The column index (starting at 1). - * @return The name or alias of the column, which can be an empty string if an error occurs. - * @throws IllegalArgumentException If the column index is invalid. - */ - public native String getColumnLabel(int column) throws IllegalArgumentException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Given the column index (starting at 1), returns the column type. - * - * @param column The column index (starting at 1). - * @return The column type, which can be: SHORT_TYPE, INT_TYPE, LONG_TYPE, - * FLOAT_TYPE, DOUBLE_TYPE, CHAR_TYPE, CHAR_NOCASE_TYPE, - * DATE_TYPE, DATETIME_TYPE, or BLOB_TYPE. - * @throws IllegalArgumentException If the column index is invalid. - */ - public native int getColumnType(int column) throws IllegalArgumentException; - - /** - * Given the column index (starting at 1), returns the name of the column type. - * - * @param column The column index (starting at 1). - * @return The name of the column type, which can be: chars, short, int, - * long, float, double, date, datetime, - * blob, or null if an error occurs. - */ - public native String getColumnTypeName(int column); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Given the column index, (starting at 1) returns the name of the table it came from. - * - * @param columnIdx The column index. - * @return The name of the table it came from or null if the column index does not exist. - * @throws IllegalArgumentException If the column index is invalid. - */ - public native String getColumnTableName(int columnIdx) throws IllegalArgumentException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Given the column name or alias, returns the name of the table it came from. - * - * @param columnName The column name. - * @return The name of the table it came from or null if the column name does not exist. - * @throws DriverException If the column was not found. - * @throws NullPointerException if the column name is null. - */ - public native String getColumnTableName(String columnName) throws DriverException, NullPointerException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@227_2: added methods to indicate if a column of a result set is not null or has default values. - /** - * Indicates if a column of the result set has default value. - * - * @param columnIndex The column index. - * @return true if the column has a default value; false, otherwise. - * @throws DriverException If the column does not have an underlining table. - */ - public native boolean hasDefaultValue(int columnIndex) throws DriverException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Indicates if a column of the result set has default value. - * - * @param columnName The column name. - * @return true if the column has a default value; false, otherwise. - * @throws DriverException If the column was not found or does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ - public native boolean hasDefaultValue(String columnName) throws DriverException, NullPointerException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Indicates if a column of the result set is not null. - * - * @param columnIndex The column index. - * @return true if the column is not null; false, otherwise. - * @throws DriverException If the column does not have an underlining table. - */ - public native boolean isNotNull(int columnIndex) throws DriverException; - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Indicates if a column of the result set is not null. - * - * @param columnName The column name. - * @return true if the column is not null; false, otherwise. - * @throws DriverException If the column was not found or does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ - public native boolean isNotNull(String columnName) throws DriverException, NullPointerException; - - // juliana@253_3: added methods to return the primary key columns of a table. - /** - * Returns the primary key column indices of a table. - * - * @param tableName The table name. - * @return null if the given table does not have primary key or an array with the column indices of the primary key. - * @throws NullPointerException if the table name is null. - */ - public native byte[] getPKColumnIndices(String tableName) throws NullPointerException; - - /** - * Returns the primary key column names of a table. - * - * @param tableName The table name. - * @return null if the given table does not have primary key or an array with the column names of the primary key. - * @throws NullPointerException if the table name is null. - */ - public native String[] getPKColumnNames(String tableName) throws NullPointerException; - - // juliana@253_4: added methods to return the default value of a column. - /** - * Returns the default value of a column. - * - * @param columnIndex The column index. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If the column index does not have an underlining table. - */ - public native String getDefaultValue(int columnIndex) throws DriverException; - - /** - * Returns the default value of a column. - * - * @param columnName The column name. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If the column name does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ - public native String getDefaultValue(String columnName) throws DriverException, NullPointerException; -} diff --git a/LitebaseSDK/src/java/litebase/RowIterator.java b/LitebaseSDK/src/java/litebase/RowIterator.java deleted file mode 100644 index 27e9fb169d..0000000000 --- a/LitebaseSDK/src/java/litebase/RowIterator.java +++ /dev/null @@ -1,550 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.*; -import totalcross.util.*; -import totalcross.io.*; - -/** - * Used to iterate through the rows of a database. It can access some attributes from the row that eases the control of which row was - * changed, deleted or is newer since a synchronization. - */ -@Deprecated -public class RowIterator -{ - /** - * Indicates if the a row was synced. - */ - public static final int ROW_ATTR_SYNCED = 0; - - /** - * Indicates if the row is new. - */ - public static final int ROW_ATTR_NEW = 1; - - /** - * Indicates if the row was updated. - */ - public static final int ROW_ATTR_UPDATED = 2; - - /** - * Indicates if the row was deleted. - */ - public static final int ROW_ATTR_DELETED = 3; - - /** - * The shift for the row attributes. - */ - private static final int ROW_ATTR_SHIFT = 30; - - /** - * The data for the current row. The whole row is included. - */ - public byte[] data; - - /** - * The rowid for the current row. - */ - public int rowid; - - /** - * The attribute for this row. It is necessary to use the constants beginning with ROW_ATTR_ to compare or assign. - */ - public int attr; - - /** - * The number of the row. Note: this must be READ ONLY. Changing it will corrupt your database. - */ - public int rowNumber; - - /** - * The table. For internal use only. - */ - private Table table; - - /** - * An stream to write and read data from the current row. - */ - protected ByteArrayStream bas; - - /** - * The data stream to read data from the current row. - */ - protected DataStreamLB basds; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * An iterator cannot be constructed directly; it must be created throught the method LitebaseConnection.getRowIterator(). - * - * @param driver The Litebase driver. - * @param tableName The name of the table for which the row iterator will be created. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - protected RowIterator(LitebaseConnection driver, String tableName) throws IOException, InvalidDateException - { - table = driver.getTable(tableName); - rowNumber = -1; - - // juliana@253_8: now Litebase supports weak cryptography. - basds = new DataStreamLB(bas = new ByteArrayStream(data = new byte[table.db.rowSize]), table.db.useCrypto); - } - - /** - * Moves to the next record and fills the data members. - * - * @return true if it is possible to iterate to the next record. Otherwise, it will return false. - * @throws DriverException If an IOException occurs. - */ - public boolean next() throws DriverException - { - checkState(); // juliana@230_27 - - PlainDB plainDb = table.db; - - try - { - if (++rowNumber < plainDb.rowCount) - { - plainDb.read(rowNumber); - int id = plainDb.basds.readInt(); - Vm.arrayCopy(plainDb.basbuf, 0, data, 0, plainDb.rowSize); - rowid = id & Utils.ROW_ID_MASK; // Masks out the attributes. - attr = ((id & Utils.ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3; - - // juliana@223_5: now possible null values are treated in RowIterator. - plainDb.bas.reset(); - table.readNullBytesOfRecord(0, false, 0); - - return true; - } - return false; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Moves to the next record with an attribute different of SYNCED. - * - * @return true if it is possible to iterate to a next record not synced. Otherwise, it will return - * false. - * @throws DriverException If an IOException occurs. - */ - public boolean nextNotSynced() throws DriverException - { - checkState(); // juliana@230_27 - - PlainDB plainDb = table.db; - - try - { - while (++rowNumber < plainDb.rowCount) - { - plainDb.read(rowNumber); - int id = plainDb.basds.readInt(); - if ((id & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_SYNCED) - continue; - Vm.arrayCopy(plainDb.basbuf, 0, data, 0, plainDb.rowSize); - rowid = id & Utils.ROW_ID_MASK; // Masks out the attributes. - attr = ((id & Utils.ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3; - - // juliana@223_5: now possible null values are treated in RowIterator. - plainDb.bas.reset(); - table.readNullBytesOfRecord(0, false, 0); - - return true; - } - - return false; - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * If the attribute is currently NEW or UPDATED, this method sets them to SYNCED. Note that if the row is DELETED, the change will be ignored. - * - * @throws DriverException If an IOException occurs. - */ - public void setSynced() throws DriverException - { - checkState(); // juliana@230_27 - - PlainDB plainDb = table.db; - plainDb.bas.reset(); - - try // The record is assumed to have been already read. - { - int id = plainDb.basds.readInt(); - int oldAttr = (((id & Utils.ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3); // guich@560_19 - int newAttr = attr = ROW_ATTR_SYNCED; - if (newAttr != oldAttr && oldAttr != ROW_ATTR_DELETED) - { - plainDb.bas.reset(); - plainDb.basds.writeInt((id & Utils.ROW_ID_MASK) | newAttr); // Sets the new attribute. - plainDb.rewrite(rowNumber); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@270_29: added RowIterator.setNotSynced(). - /** - * Forces the attribute to be NEW. This method will be useful if a row was marked as synchronized but was not sent to server for some problem. - * If the row is marked as DELETED, its attribute won't be changed. - * - * @throws DriverException If an IOException occurs. - */ - public void setNotSynced() - { - checkState(); // juliana@230_27 - - PlainDB plainDb = table.db; - plainDb.bas.reset(); - - try - { - int id = plainDb.basds.readInt(); - int oldAttr = (((id & Utils.ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3); // guich@560_19 - int newAttr = attr = Utils.ROW_ATTR_NEW; - if (newAttr != oldAttr && oldAttr != ROW_ATTR_DELETED) - { - plainDb.bas.reset(); - plainDb.basds.writeInt((id & Utils.ROW_ID_MASK) | newAttr); // Sets the new attribute. - plainDb.rewrite(rowNumber); - } - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Closes this iterator. - * - * @throws DriverException if an IOException occurs. - */ - public void close() - { - checkState(); // juliana@230_27 - - // juliana@227_22: RowIterator.close() now flushes the setSynced() calls. - NormalFile dbFile = (NormalFile)table.db.db; - if (dbFile.cacheIsDirty) - try - { - dbFile.flushCache(); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - - data = null; - table = null; - } - - /** - * Resets the counter to zero so it is possible to restart to fetch records. - */ - public void reset() - { - rowNumber = -1; - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns a short contained in the current row. - * - * @param column The short column index, starting from 1. - * @return The value of the column or 0 if the column is null. - * @throws DriverException If an IOException occurs. - */ - public short getShort(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.SHORT, SQLElement.UNDEFINED)) - return 0; - return basds.readShort(); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns an integer contained in the current row. - * - * @param column The integer column index, starting from 1. - * @return The value of the column or 0 if the column is null. - * @throws DriverException If an IOException occurs. - */ - public int getInt(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.INT, SQLElement.UNDEFINED)) - return 0; - return basds.readInt(); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns a long integer contained in the current row. - * - * @param column The long integer column index, starting from 1. - * @return The value of the column or 0 if the column is null. - * @throws DriverException If an IOException occurs. - */ - public long getLong(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.LONG, SQLElement.UNDEFINED)) - return 0; - return basds.readLong(); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns a floating point number contained in the current row. - * - * @param column The floating point number column index, starting from 1. - * @return The value of the column or 0 if the column is null. - * @throws DriverException If an IOException occurs. - */ - public double getFloat(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.FLOAT, SQLElement.UNDEFINED)) - return 0; - return basds.readFloat(); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns a double precision floating point number contained in the current row. Returns 0 if the column is null. - * - * @param column The double precision floating point number column index, starting from 1. - * @return The value of the column or 0 if the column is null. - * @throws DriverException If an IOException occurs. - */ - public double getDouble(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.DOUBLE, SQLElement.UNDEFINED)) - return 0; - return basds.readDouble(); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Returns a string contained in the current row. Note that this method must be used only with CHAR types. - * - * @param column The string column index, starting from 1. - * @return The value of the column or null if the column is null. - * @throws DriverException If an IOException occurs. - */ - public String getString(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.CHARS, SQLElement.CHARS_NOCASE)) - return null; - - PlainDB plainDB = table.db; - plainDB.dbo.setPos(basds.readInt()); // Finds the string position in the .dbo. - return plainDB.loadString(); - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - /** - * Returns a blob contained in the current row. - * - * @param column The blob column index, starting from 1. - * @return The value of the column or null if the column is null - * @throws DriverException If an IOException occurs. - */ - public byte[] getBlob(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.BLOB, SQLElement.UNDEFINED)) - return null; - - PlainDB db = table.db; - DataStreamLB ds = db.dsdbo; // juliana@253_8: now Litebase supports weak cryptography. - db.dbo.setPos(basds.readInt()); // Finds the blob position in the .dbo. - - // Reads it. - byte[] object = new byte[ds.readInt()]; // Finds the blob size. - ds.readBytes(object); - return object; - - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns a date contained in the current row. - * - * @param column The date column index, starting from 1. - * @return The value of the column or null if the column is null - * @throws DriverException If an IOException or an InvalidDateException occurs. - */ - public Date getDate(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.DATE, SQLElement.UNDEFINED)) - return null; - return new Date(basds.readInt()); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - catch (InvalidDateException exception) - { - return null; - } - } - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Returns a datetime contained in the current row. Returns null if the column is null. - * - * @param column The datetime column index, starting from 1. - * @return The value of the column or null if the column is null. - * @throws DriverException If an IOException occurs. - */ - public Time getDateTime(int column) throws DriverException - { - try - { - if (readColumn(column, SQLElement.DATETIME, SQLElement.UNDEFINED)) - return null; - return new Time(basds.readInt(), basds.readInt()); // Reads the value. - } - catch (IOException exception) - { - throw new DriverException(exception); - } - } - - // juliana@223_5: now possible null values are treated in RowIterator. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Indicates if this column has a NULL. - * - * @param column The column index, starting from 1. - * @return true if the value is SQL NULL; false, otherwise. - * @throws IllegalArgumentException If the column index is invalid. - */ - public boolean isNull(int column) throws IllegalArgumentException - { - checkState(); - - Table tableAux = table; - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a - // DriverException. - if (column < 0 || column >= tableAux.columnCount) - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + column); - - return (table.columnNulls[0][column >> 3] & (1 << (column & 7))) != 0; - } - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - /** - * Checks if the driver or the row iterator are closed. - * - * @throws IllegalStateException if one of them is closed. - */ - private void checkState() - { - if (table == null) - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_ROWITERATOR_CLOSED)); - if (table.db == null) - throw new IllegalStateException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DRIVER_CLOSED)); - } - - /** - * Checks if the driver or the row iterator is closed, if the column index is invalid or if the type of the column requested is imcompatible with - * the get method type and reads the requested value. - * - * @param column The column index. - * @param type1 The main column type of the method. - * @param type2 Used only for strings to test if the column is a char nocase. - * @return true if the value stored is SQL NULL; false, otherwise. - * @throws DriverException If the column is not of type requested. - * @throws IllegalArgumentException If the column index is invalid. - * @throws IOException If an internal method throws it. - */ - private boolean readColumn(int column, int type1, int type2) throws IllegalArgumentException, DriverException, IOException - { - checkState(); // juliana@230_27 - - Table tableAux = table; - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a - // DriverException. - if (column < 0 || column >= tableAux.columnCount) - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NUMBER) + column); - - int type = tableAux.columnTypes[column]; - if (type != type1 && type != type2) // Check the column type. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - bas.setPos(tableAux.columnOffsets[column]); // Finds the value position. - - // juliana@223_5: now possible null values are treated in RowIterator. - // Treats possible null values. - return (tableAux.columnNulls[0][column >> 3] & (1 << (column & 7))) != 0; - } -} diff --git a/LitebaseSDK/src/java/litebase/RowIterator4D.java b/LitebaseSDK/src/java/litebase/RowIterator4D.java deleted file mode 100644 index 5964ccc45a..0000000000 --- a/LitebaseSDK/src/java/litebase/RowIterator4D.java +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.Time; -import totalcross.util.Date; - -/** - * Native class used to iterate through the rows of a database. It can access some attributes from the row that eases the control of which row was - * changed, deleted or is newer since a synchronization. - */ -@Deprecated -public class RowIterator4D -{ - /** - * Indicates if the a row was synced. - */ - public static final int ROW_ATTR_SYNCED = 0; - - /** - * Indicates if the row is new. - */ - public static final int ROW_ATTR_NEW = 1; - - /** - * Indicates if the row was updated. - */ - public static final int ROW_ATTR_UPDATED = 2; - - /** - * Indicates if the row was deleted. - */ - public static final int ROW_ATTR_DELETED = 3; - - /** - * The data for the current row. The whole row is included. - */ - public byte[] data; - - /** - * The rowid for the current row. - */ - public int rowid; - - /** - * The connection with Litebase. - */ - Object driver; - - /** - * The attribute for this row. It is necessary to use the constants beginning with ROW_ATTR_ to compare or assign. - */ - public int attr; - - /** - * The number of the row. Note: this must be READ ONLY. Changing it will corrupt your database. - */ - public int rowNumber; - - /** - * The table. For internal use only. - */ - long table; - - // juliana@230_11: Litebase public class constructors are now not public any more. - /** - * The constructor. - */ - private RowIterator4D() {} - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Moves to the next record and fills the data members. - * - * @return true if it is possible to iterate to the next record. Otherwise, it will return false. - */ - public native boolean next(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Moves to the next record with an attribute different of SYNCED. - * - * @return true if it is possible to iterate to a next record not synced. Otherwise, it will return false. - */ - public native boolean nextNotSynced(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * If the attribute is currently NEW or UPDATED, this method sets them to SYNCED. Note that if the row is DELETED, the change will be ignored. - */ - public native void setSynced(); - - // juliana@270_29: added RowIterator.setNotSynced(). - /** - * Forces the attribute to be NEW. This method will be useful if a row was marked as synchronized but was not sent to server for some problem. - * If the row is marked as DELETED, its attribute won't be changed. - */ - public native void setNotSynced(); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Closes this iterator. - */ - public native void close(); - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Resets the counter to zero so it is possible to restart to fetch records. - */ - public native void reset(); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a short contained in the current row. - * - * @param column The short column index, starting from 1. - * @return The value of the column or 0 if the column is null. - */ - public native short getShort(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns an integer contained in the current row. - * - * @param column The integer column index, starting from 1. - * @return The value of the column or 0 if the column is null. - */ - public native int getInt(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a long integer contained in the current row. - * - * @param column The long integer column index, starting from 1. - * @return The value of the column or 0 if the column is null. - */ - public native long getLong(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a floating point number contained in the current row. - * - * @param column The floating point number column index, starting from 1. - * @return The value of the column or 0 if the column is null. - */ - public native double getFloat(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a double precision floating point number contained in the current row. - * - * @param column The double precision floating point number column index, starting from 1. - * @return The value of the column or 0 if the column is null. - */ - public native double getDouble(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a string contained in the current row. - * - * @param column The string column index, starting from 1. - * @return The value of the column or null if the column is null. - */ - public native String getString(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a blob contained in the current row. - * - * @param column The blob column index, starting from 1. - * @return The value of the column or null if the column is null. - */ - public native byte[] getBlob(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a date contained in the current row. - * - * @param column The date column index, starting from 1. - * @return The value of the column or null if the column is null. - */ - public native Date getDate(int column); - - // juliana@223_5: now possible null values are treated in RowIterator. - /** - * Returns a datetime contained in the current row. - * - * @param column The datetime column index, starting from 1. - * @return The value of the column or null if the column is null. - */ - public native Time getDateTime(int column); - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a - // DriverException. - // juliana@223_5: now possible null values are treated in RowIterator. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - /** - * Indicates if this column has a NULL. - * - * @param column The column index, starting from 1. - * @return true if the value is SQL NULL; false, otherwise. - * @throws IllegalArgumentException If the column index is invalid. - */ - public native boolean isNull(int column) throws IllegalArgumentException; -} diff --git a/LitebaseSDK/src/java/litebase/SQLBooleanClause.java b/LitebaseSDK/src/java/litebase/SQLBooleanClause.java deleted file mode 100644 index dfdc3f661f..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLBooleanClause.java +++ /dev/null @@ -1,850 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Internal use only. Represents a boolean clause (WHERE or HAVING) in a SQL query. - */ -@Deprecated -class SQLBooleanClause -{ - /** - * The maximum number of indexes to be applied. - */ - static final int MAX_NUM_INDEXES_APPLIED = 32; - - /** - * Resulting boolean clause expression tree. - */ - SQLBooleanClauseTree expressionTree; - - /** - * The associated result set. - */ - ResultSet resultSet; - - // Other information used during the colum binding process. - /** - * A list of all fields referenced in the boolean clause. - */ - SQLResultSetField[] fieldList = new SQLResultSetField[SQLElement.MAX_NUM_COLUMNS]; - - /** - * The number of fields. - */ - int fieldsCount; - - /** - * Table that maps the field name to an index in the field list. - */ - IntHashtable fieldName2Index = new IntHashtable(SQLElement.MAX_NUM_COLUMNS); - - /** - * The list of trees that contains the parameter list of the boolean clause. - */ - SQLBooleanClauseTree[] paramList = new SQLBooleanClauseTree[SQLElement.MAX_NUM_PARAMS]; - - /** - * The length of the parameter list. - */ - int paramCount; - - /** - * The columns whose indexes were applied to the boolean clause. A column can be listed more than once, in case it is listed more than once in the - * boolean clause. - */ - byte[] appliedIndexesCols = new byte[MAX_NUM_INDEXES_APPLIED]; - - /** - * The constant values to be used by the indexes that were applied to the boolean clause. - */ - SQLBooleanClauseTree[] appliedIndexesValueTree = new SQLBooleanClauseTree[MAX_NUM_INDEXES_APPLIED]; - - /** - * The relational operators to be used by the indexes that were applied to the boolean clause. - */ - byte[] appliedIndexesRelOps = new byte[MAX_NUM_INDEXES_APPLIED]; - - /** - * The number of indexes to be applied. - */ - int appliedIndexesCount; - - /** - * The boolean operator to be used to combine the result set of each index. Can be either SQLElement.OP_BOOLEAN_AND, - * SQLElement.OP_BOOLEAN_OR, or SQLElement.OP_BOOLEAN_NONE (in case only one index was used). - */ - int appliedIndexesBooleanOp; - - /** - * Indicates if it is a where clause or a having clause. - */ - boolean isWhereClause = true; - - /** - * A backup of the expression tree. - */ - SQLBooleanClauseTree expressionTreeBak; - - /** - * The tables of the correspondent indexes. - */ - Table[] appliedIndexesTables = new Table[MAX_NUM_INDEXES_APPLIED]; - - /** - * Indicates if the result set will be indexed. - */ - private int appliedIndexRs = -1; - - /** - * Type of the where clause: AND of different result sets = 0, and OR of different result sets = 1. - */ - int type = -1; - - /** - * The composed indices applied. - */ - ComposedIndex[] appliedComposedIndexes = new ComposedIndex[MAX_NUM_INDEXES_APPLIED]; - - /** - * Temporary date. - */ - Date tempDate = new Date(); - - // juliana@226_3: improved index application. - /** - * Applies the table indices to the boolean clause. The method will possibly transform the SQL boolean tree, eliminating the branches that can be - * resolved through the table indexes. - * - * @param tableIndices The table indices; each position in the array relates to a column in the table; a null value indicates no - * index on that column. - * @param hasComposedIndex Indicates if the table has a composed index. - * @return true, if any table index was applied to the boolean clause; false, otherwise. - */ - boolean sqlbooleanclauseApplyTableIndices(Index[] tableIndices, boolean hasComposedIndex) - { - // juliana@223_2: corrected a bug that would throw an exception if the where clause if of the form 1 = 1. - if (!isWhereClause || fieldList.length == 0) // Indices can only be applied to the where clause with fields. - return false; - - int columnsCount = tableIndices.length; - - if (!hasComposedIndex) // Verifies if it has simple indices. - { - while (--columnsCount >= 0 && tableIndices[columnsCount] == null); - - if (columnsCount < 0) // If there are no indices, returns. - return false; - } - - // Traverses the tree, from the parent to the rightmost tree until the boolean operator changes. To simplify the algorithm and considering - // that complex boolean expressions (the ones enclosed by parenthesis) are always connected to the left branch), only the branches connected - // to the right side of the tree are candidates to be replaced by table indexing. - SQLBooleanClauseTree curTree = expressionTree, - originalTree; - SQLBooleanClauseTree[] indexesValueTree; - SQLBooleanClauseTree leftTree; - int curOperandType, - leftOperandType, - rightOperandType, - count, - countAppliedIndices = 0, - i, - j; - boolean appliedComposedIndex, - isLeft = false; - byte[] columns; - byte[] operators; - byte[] columnsComp; - Table table = fieldList[0].table; - ComposedIndex[] composedIndices = table.composedIndices; - ComposedIndex[] appliedCI = appliedComposedIndexes; - ComposedIndex currCompIndex; - - if (hasComposedIndex) - { - indexesValueTree = new SQLBooleanClauseTree[fieldsCount]; - columns = new byte[fieldsCount]; - operators = new byte[fieldsCount]; - } - else - { - indexesValueTree = null; - columns = operators = null; - } - - appliedIndexesBooleanOp = SQLElement.OP_NONE; - - while (curTree != null) - { - leftTree = curTree.leftTree; - - switch (curOperandType = curTree.operandType) // Checks the operand type. - { - // juliana@214_4: nots were removed. - - case SQLElement.OP_BOOLEAN_AND: - case SQLElement.OP_BOOLEAN_OR: - - // Checks if the boolean connector is different than the previous one. If so, leaves the loop, since for now, for simplicty, the - // algorithm does not combine different boolean operators. - if (appliedIndexesBooleanOp != SQLElement.OP_NONE && appliedIndexesBooleanOp != curOperandType) - { - curTree = null; - break; - } - - appliedIndexesBooleanOp = curOperandType; - - // Checks if the left tree has a simple boolean operand. If so, try to apply an index on it. - leftOperandType = leftTree.operandType; - appliedComposedIndex = false; - - // juliana@250_2: corrected a problem of composed indices not returning the expected result. - if ((leftOperandType >= SQLElement.OP_REL_LESS && leftOperandType <= SQLElement.OP_REL_DIFF) - || (leftTree.patternMatchType == SQLBooleanClauseTree.PAT_MATCH_STARTS_WITH - && (leftOperandType == SQLElement.OP_PAT_MATCH_LIKE || leftOperandType == SQLElement.OP_PAT_MATCH_NOT_LIKE))) - { - if (hasComposedIndex && curOperandType == SQLElement.OP_BOOLEAN_AND && !isLeft) // First verifies if it can apply a composed index. - { - originalTree = curTree; - count = 0; - Convert.fill(indexesValueTree, 0, fieldsCount, null); - Convert.fill(columns, 0, fieldsCount, 0); - Convert.fill(operators, 0, fieldsCount, 0); - - while (true) - { - leftTree.getBranchProperties(columns, operators, indexesValueTree, count); - - // Limitation; Composed index only for EQUALS. A composed index can't be applied if the column is not part of the index. - if (count >= operators.length || operators[count] != SQLElement.OP_REL_EQUAL) - { - count = 0; - break; // Doesn't apply the composed index. - } - - count++; - - // Verifies if the right operator is one of the above. - if ((rightOperandType = curTree.rightTree.operandType) == SQLElement.OP_BOOLEAN_AND) - curTree = curTree.rightTree; - else - if ((rightOperandType >= SQLElement.OP_REL_EQUAL && rightOperandType <= SQLElement.OP_REL_LESS_EQUAL) - || (curTree.rightTree.patternMatchType == SQLBooleanClauseTree.PAT_MATCH_STARTS_WITH - && (rightOperandType == SQLElement.OP_PAT_MATCH_LIKE || rightOperandType == SQLElement.OP_PAT_MATCH_NOT_LIKE))) - { - curTree.rightTree.getBranchProperties(columns, operators, indexesValueTree, count); - - // Limitation: composed index only for EQUALS. - if (count >= operators.length || operators[count] != SQLElement.OP_REL_EQUAL) - { - count = 0; - break; // Doesn't apply the composed index. - } - count++; - break; - } - else - break; // The next operator is an OR, ends the loop. - } - if (count >= 2) // It has an AND operator on at least 2 fields? - { - i = table.numberComposedIndices; - while (--i >= 0) - { - currCompIndex = composedIndices[i]; - appliedComposedIndex = false; - columnsComp = currCompIndex.columns; - - // juliana@202_6: A composed index can only be used if all its columns are "ANDED" in the where clause. - j = currCompIndex.columns.length; - if (columns.length >= j) - { - appliedComposedIndex = true; - while (--j >= 0) - if (columns[j] != columnsComp[j]) - { - appliedComposedIndex = false; - break; - } - } - - if (appliedComposedIndex) - { - appliedCI[appliedIndexesCount] = currCompIndex; - curTree = sqlbooleanclauseApplyComposedIndexToBranch(originalTree, columns, operators, indexesValueTree, - currCompIndex); - break; - } - else - curTree = originalTree; - } - } - - } - if (!appliedComposedIndex) - sqlbooleanclauseApplyIndexToBranch(curTree.leftTree, tableIndices, isLeft); - } - if (!appliedComposedIndex) // Goes to the right tree. - { - if (isLeft) - curTree = leftTree; - else - curTree = curTree.rightTree; - } - break; - - // Reached the rightmost node. Triwal to apply the index and ends the loop. - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - case SQLElement.OP_PAT_MATCH_LIKE: - if (curTree.rightTree.patternMatchType != SQLBooleanClauseTree.PAT_MATCH_STARTS_WITH - && curTree.rightTree.patternMatchType != SQLBooleanClauseTree.PAT_MATCH_EQUAL) - { - curTree = null; - break; - } - // else falls through. - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - case SQLElement.OP_REL_LESS_EQUAL: - case SQLElement.OP_REL_DIFF: - countAppliedIndices = appliedIndexesCount; - sqlbooleanclauseApplyIndexToBranch(curTree, tableIndices, isLeft); - if (countAppliedIndices == appliedIndexesCount) - curTree = null; - else - curTree = expressionTree; - break; - - default: // Anything else, stops the loop. - curTree = null; - } - - if (appliedIndexesCount == MAX_NUM_INDEXES_APPLIED) // If the number of indexes to be applied reached the limit leaves the loop. - break; - - if (curTree == null && appliedIndexesCount == 0 && !isLeft) - { - isLeft = true; - curTree = expressionTree; - } - } - - return appliedIndexesCount > 0; - } - - /** - * Tries to apply an index to a branch of the expression tree that contains a relational expression. - * - * @param branch A branch of the expression tree. - * @param indexMap An index bitmap. - * @param isLeft Indicates if the index is being applied to the left branch. - */ - private void sqlbooleanclauseApplyIndexToBranch(SQLBooleanClauseTree branch, Index[] indexesMap, boolean isLeft) - { - int relationalOp = branch.operandType; - - // Checks if the relational expression involves a column and a constant. - SQLBooleanClauseTree left = branch.leftTree, - right = branch.rightTree; - boolean leftIsColumn = (left.operandType == SQLElement.OP_IDENTIFIER), - rightIsColumn = (right.operandType == SQLElement.OP_IDENTIFIER); - byte[] appliedIndexesColsAux = appliedIndexesCols; - byte[] appliedIndexesRelOpsAux = appliedIndexesRelOps; - SQLBooleanClauseTree[] appliedIndexesValueTreeAux = appliedIndexesValueTree; - SQLResultSetField[] list = fieldList; - - if (leftIsColumn != rightIsColumn) - { - int column = (leftIsColumn? left.colIndex : right.colIndex); - - int i = fieldsCount; - while (--i >= 0) - if (list[i].tableColIndex == column && list[i].isDataTypeFunction) // An index cannot be applied to a function in the where clause. - return; - - if (indexesMap[column] != null) // Checks if the column is indexed. - { - // Adds the index to the list of applied indexes. - int n = appliedIndexesCount++; - appliedIndexesColsAux[n] = (byte)column; - appliedIndexesValueTreeAux[n] = leftIsColumn? right : left; - appliedIndexesRelOpsAux[n] = (byte)relationalOp; - - SQLBooleanClauseTree parent = branch.parent; - - if (parent == null) // Removes the branch from the expression tree. - expressionTree = null; // The branch has no parent. So, no expression tree will be left. - else - { - SQLBooleanClauseTree sibling = (branch == parent.leftTree? parent.rightTree : parent.leftTree), - grandParent = parent.parent; - - // Links the branch sibling to its grandparent, removing the branch from the tree, as result. - if (grandParent == null) - expressionTree = sibling; - else if (isLeft) - grandParent.leftTree = sibling; - else - grandParent.rightTree = sibling; - sibling.parent = grandParent; - } - } - } - } - - /** - * Applies the composed index and removes the correspondent branch of the tree. - * - * @param branch A branch of the expression tree. - * @param columns The columns present in the expression tree. - * @param operators The operators of the expression tree. - * @param indexesValueTree The part of the tree that uses indices. - * @param ci The composed index. - * @return The current branch of the tree. - */ - private SQLBooleanClauseTree sqlbooleanclauseApplyComposedIndexToBranch(SQLBooleanClauseTree branch, byte[] columns, byte[] operators, - SQLBooleanClauseTree[] indexesValueTree, ComposedIndex ci) - { - int i = -1, - n, - length = ci.columns.length; - byte[] appliedIndexesColsAux = appliedIndexesCols; - byte[] appliedIndexesRelOpsAux = appliedIndexesRelOps; - SQLBooleanClauseTree[] appliedIndexesValueTreeAux = appliedIndexesValueTree; - - // Checks if the column is indexed. - while (++i < length) - { - // Adds the index to the list of applied indexes. - appliedIndexesColsAux[n = appliedIndexesCount++] = columns[i]; - appliedIndexesValueTreeAux[n] = indexesValueTree[i]; - appliedIndexesRelOpsAux[n] = operators[i]; - } - - SQLBooleanClauseTree parent = branch.parent, - root = branch; - - while (--i >= 0) - branch = branch.rightTree; - - if (parent == null) - { - branch.parent = null; - if (branch.operandType != SQLElement.OP_BOOLEAN_AND && branch.operandType != SQLElement.OP_BOOLEAN_OR) // Is the end of the root? - branch = null; - expressionTree = branch; - } - else - { - branch.parent = parent; - root.parent.rightTree = branch; - } - return branch; - } - - // juliana@253_7: improved index application on filters when using joins. - // juliana@226_3: improved index application. - /** - * Applies the table indexes to the boolean clause. The method will possibly transform the SQL boolean tree, to eliminate the branches that can be - * resolved through the table indexes. - * - * @return true, if any table index was applied to the boolean clause; false, otherwise. - */ - boolean sqlbooleanclauseApplyTableIndexesJoin() - { - if (!isWhereClause) // Indexes can only be applied to a where clause. - return false; - - // Traverses the tree, from the parent to the rightmost tree, until the boolean operator changes. To simplify the algorithm and considering - // that complex boolean expressions (the ones enclosed by parenthesis) are always connected to the left branch), only the branches connected to - // the right side of the tree are candidates to be replaced by table indexing. - SQLBooleanClauseTree curTree = expressionTree, - leftTree; - int curOperandType, - leftOperandType, - countAppliedIndices = 0; - boolean isLeft = false; - - appliedIndexesBooleanOp = SQLElement.OP_NONE; - while (curTree != null) - { - leftTree = curTree.leftTree; - - switch (curOperandType = curTree.operandType) // Checks the type of operand. - { - // juliana@214_4: nots were removed. - - case SQLElement.OP_BOOLEAN_AND: - case SQLElement.OP_BOOLEAN_OR: - - // Checks if the boolean connector is different than the previous one. If so, leaves the loop, since for now, for simplicty, the - // algorithm does not combine different boolean operators. - if (appliedIndexesBooleanOp != SQLElement.OP_NONE && appliedIndexesBooleanOp != curOperandType) - { - curTree = null; - break; - } - - appliedIndexesBooleanOp = curOperandType; - if (appliedIndexRs == -1) - appliedIndexRs = leftTree.indexRs; - - // Checks if the left tree has a simple boolean operand. If so, tries to apply an index on it. - leftOperandType = leftTree.operandType; - - if ((leftOperandType >= SQLElement.OP_REL_EQUAL && leftOperandType <= SQLElement.OP_REL_LESS_EQUAL) - || ((leftOperandType == SQLElement.OP_PAT_MATCH_LIKE || leftOperandType == SQLElement.OP_PAT_MATCH_NOT_LIKE) - && leftTree.patternMatchType == SQLBooleanClauseTree.PAT_MATCH_STARTS_WITH)) - sqlbooleanclauseApplyIndexToBranchJoin(leftTree, isLeft); - - if (curTree.rightTree.indexRs != appliedIndexRs) - if (curOperandType == SQLElement.OP_BOOLEAN_AND) - type = Utils.WC_TYPE_AND_DIFF_RS; - else // 'OR' of different result sets, leaves the loop. - - { - type = Utils.WC_TYPE_OR_DIFF_RS; - curTree = null; - break; - } - - if (isLeft) - curTree = leftTree; - else - curTree = curTree.rightTree; - break; - - // Reached the rightmost node. Tries to apply the index and ends the loop. - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - case SQLElement.OP_PAT_MATCH_LIKE: - if (curTree.rightTree.patternMatchType != SQLBooleanClauseTree.PAT_MATCH_STARTS_WITH - && curTree.rightTree.patternMatchType != SQLBooleanClauseTree.PAT_MATCH_EQUAL) - { - curTree = null; - break; - } - // else fall through. - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - case SQLElement.OP_REL_LESS_EQUAL: - case SQLElement.OP_REL_DIFF: - countAppliedIndices = appliedIndexesCount; - sqlbooleanclauseApplyIndexToBranchJoin(curTree, isLeft); - if (countAppliedIndices == appliedIndexesCount) - curTree = null; - else - curTree = expressionTree; - break; - - default: // Anything else, stops the loop. - curTree = null; - } - - if (appliedIndexesCount == MAX_NUM_INDEXES_APPLIED) // If the number of indexes to be applied reached the limit, leaves the loop. - break; - - if (curTree == null && appliedIndexesCount == 0 && !isLeft) - { - isLeft = true; - curTree = expressionTree; - } - } - - return appliedIndexesCount > 0; - } - - // juliana@253_7: improved index application on filters when using joins. - /** - * Tries to apply an index to a branch of the expression tree that contains a relational expression. - * - * @param branch The branch of the expression tree. - * @param isLeft Indicates if the index is being applied to the left branch. - */ - private void sqlbooleanclauseApplyIndexToBranchJoin(SQLBooleanClauseTree branch, boolean isLeft) - { - int relationalOp = branch.operandType; - - // Checks if the relational expression involves a column and a constant. - SQLBooleanClauseTree left = branch.leftTree, - right = branch.rightTree, tree; - boolean leftIsColumn = (left.operandType == SQLElement.OP_IDENTIFIER), - rightIsColumn = (right.operandType == SQLElement.OP_IDENTIFIER); - - if (branch.bothAreIdentifier) - { - tree = right; - SQLResultSetField field = fieldList[fieldName2Index.get(tree.nameSqlFunctionHashCode != 0? - tree.nameSqlFunctionHashCode : tree.nameHashCode, -1)]; - - if (field.table.columnIndices[right.colIndex] != null) - right.hasIndex = true; - } - - if (leftIsColumn != rightIsColumn) - { - int column; - if (leftIsColumn) - { - column = left.colIndex; - tree = left; - } - else - { - column = right.colIndex; - tree = right; - } - SQLResultSetField field = fieldList[fieldName2Index.get(tree.nameSqlFunctionHashCode != 0 ? - tree.nameSqlFunctionHashCode : tree.nameHashCode, -1)]; - - // juliana@285_1: solved a possible wrong result if the query had join and a filter with function in a column with an index. - // Checks if the column is indexed. - if (field.table.columnIndices[column] != null && field.isDataTypeFunction == false) - { - // Adds the index to the list of applied indexes. - int n = appliedIndexesCount++; - appliedIndexesCols[n] = (byte)column; - appliedIndexesValueTree[n] = leftIsColumn ? right : left; - appliedIndexesRelOps[n] = (byte)relationalOp; - appliedIndexesTables[n] = field.table; - - SQLBooleanClauseTree parent = branch.parent; - - if (parent == null) // Removes the branch from the expression tree. - expressionTree = null; // The branch has no parent. So, no expression tree will be left. - else - { - SQLBooleanClauseTree sibling = (branch == parent.leftTree? parent.rightTree : parent.leftTree); - SQLBooleanClauseTree grandParent = parent.parent; - - // Links the branch sibling to its grandparent, removing the branch from the tree, as result. - if (grandParent == null) - expressionTree = sibling; - else if (isLeft) - grandParent.leftTree = sibling; - else - grandParent.rightTree = sibling; - sibling.parent = grandParent; - } - } - } - } - - /** - * Evaluate the boolean clause, accordingly to values of the current record of the given ResultSet. - * - * @throws DriverException if a parameter is not defined. - */ - void sqlBooleanClausePreVerify() throws DriverException - { - // Checks if there are parameters defined in the clause and if all them had their values assigned. - int i = paramCount; - while (--i >= 0) - if (!paramList[i].isParamValueDefined) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAMETER_NOT_DEFINED) + i); - } - - /** - * Evaluates the boolean clause, accordingly to values of the current record of the given ResultSet. - * - * @param rs the ResultSet used for the evaluation. - * @return true, if the current record of the result set satisfies the boolean clause; false, otherwise. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - boolean sqlBooleanClauseSatisfied(ResultSet rs) throws IOException, InvalidDateException, InvalidNumberException - { - resultSet = rs; - return expressionTree.booleanTreeEvaluate(); - } - - /** - * Binds the column information of the underlying table list to the boolean clause. - * - * @param names2Index IntHashtable that maps the column names to the column indexes. - * @param columnTypes The data types of each column. - * @param tableList The table list of the select clause. - * @throws InvalidNumberException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void bindColumnsSQLBooleanClause(IntHashtable names2Index, byte[] columnTypes, SQLResultSetTable[] tableList) throws InvalidDateException, InvalidNumberException - { - // These two are only used in the expressionTree. - if (tableList != null) // The having clause has already been verified. - verifyColumnNamesOnTableList(fieldList, tableList); - else // Rearranges the having clause. - { - int i = fieldList.length; - SQLResultSetField field; - while (--i >= 0) - { - field = fieldList[i]; - field.tableColIndex = names2Index.get(field.aliasHashCode, -1); // Sets the column indexes of the temp table. - - if (field.sqlFunction == SQLElement.FUNCTION_DT_NONE) - field.dataType = columnTypes[field.tableColIndex]; - } - } - expressionTree = SQLBooleanClauseTree.removeNots(expressionTree); // juliana@214_4 - expressionTree.bindColumnsSQLBooleanClauseTree(); // Binds the field information in the tree to the table columns. - } - - /** - * Verifies if the column names are correct and belongs to the table list and is used only to verify if where clause and having clause field list - * is the field list of the where/having clause. - * - * @param sqlBooleanClauseFieldList The field list of the where/having clause. - * @param tableList The table list. - * @throws SQLParseException If there is an unknown or an ambiguos column name. - */ - void verifyColumnNamesOnTableList(SQLResultSetField[] sqlBooleanClauseFieldList, SQLResultSetTable[] tableList) throws SQLParseException - { - int size = sqlBooleanClauseFieldList.length, - index = -1, - i, - hashAliasTableName; - boolean foundFirst; - Table currentTable; - SQLResultSetField field; - - while (--size >= 0) - { - field = sqlBooleanClauseFieldList[size]; - - if (field.tableName != null) - { - // Verifies if it is a valid table name. - hashAliasTableName = field.tableName.hashCode(); - currentTable = null; - i = tableList.length; - while (--i >= 0) - if (tableList[i].aliasTableNameHashCode == hashAliasTableName) - { - currentTable = tableList[i].table; - break; - } - - if (currentTable == null - || (index = currentTable.htName2index.get(field.tableColName.hashCode(), -1)) == -1) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - - field.table = currentTable; - field.tableColIndex = index; - if (field.sqlFunction == SQLElement.FUNCTION_DT_NONE) - field.dataType = currentTable.columnTypes[index]; - else - field.parameter.dataType = currentTable.columnTypes[index]; - field.indexRs = i; - } - else - { - // Verifies if the column name in field list is ambiguous. - currentTable = null; - foundFirst = false; - i = tableList.length; - while (--i >= 0) - { - currentTable = tableList[i].table; - index = currentTable.htName2index.get(field.tableColHashCode, -1); - if (index != -1) - if (foundFirst) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_AMBIGUOUS_COLUMN_NAME) + field.alias); - else - { - foundFirst = true; - field.table = currentTable; - field.tableColIndex = index; - if (field.sqlFunction == SQLElement.FUNCTION_DT_NONE) - field.dataType = currentTable.columnTypes[index]; - else - field.parameter.dataType = currentTable.columnTypes[index]; - field.indexRs = i; - } - } - if (!foundFirst) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - } - } - } - - /** - * Binds the column information of the underlying table to the boolean clause. - * - * @param names2Index IntHashtable that maps the column names to the column indexes. - * @param columnTypes The data types of each column. - * @param rsTable The SQLResultSetTable table of the update or delete statement. - * @throws InvalidNumberException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void bindColumnsSQLBooleanClause(IntHashtable names2Index, byte[] columnTypes, SQLResultSetTable rsTable) throws InvalidDateException, InvalidNumberException - { - verifyColumnNamesOnTable(fieldList, rsTable); // These two are only used in the expressionTree. - expressionTree = SQLBooleanClauseTree.removeNots(expressionTree); // juliana@214_4 - expressionTree.bindColumnsSQLBooleanClauseTree(); // Binds the field information in the tree to the table columns. - } - - /** - * Verifies if the column names are correct and belongs to the table and is used only to verify if where clause field list is the field list of - * the where clause. - * - * @param sqlBooleanClauseFieldList The field list of the where clause. - * @param rsTable The SQLResultSetTable table of the update or delete statement. - */ - void verifyColumnNamesOnTable(SQLResultSetField[] sqlBooleanClauseFieldList, SQLResultSetTable rsTable) - { - int size = sqlBooleanClauseFieldList.length, - index = -1; - Table currentTable; - SQLResultSetField field; - - while (--size >= 0) - { - field = sqlBooleanClauseFieldList[size]; - - if (field.tableName != null) - { - // Verifies if it is a valid table name. - if (rsTable.aliasTableNameHashCode != field.tableName.hashCode() - || (index = (currentTable = rsTable.table).htName2index.get(field.tableColName.hashCode(), -1)) == -1) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - field.table = currentTable; - field.tableColIndex = index; - if (field.sqlFunction == SQLElement.FUNCTION_DT_NONE) - field.dataType = currentTable.columnTypes[index]; - else - field.parameter.dataType = currentTable.columnTypes[index]; - } - else - { - // Verifies if the column name in field list is ambiguous. - currentTable = rsTable.table; - index = currentTable.htName2index.get(field.tableColHashCode, -1); - if (index != -1) - { - field.table = currentTable; - field.tableColIndex = index; - if (field.sqlFunction == SQLElement.FUNCTION_DT_NONE) - field.dataType = currentTable.columnTypes[index]; - else - field.parameter.dataType = currentTable.columnTypes[index]; - } - else - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - } - } - } - -} diff --git a/LitebaseSDK/src/java/litebase/SQLBooleanClauseTree.java b/LitebaseSDK/src/java/litebase/SQLBooleanClauseTree.java deleted file mode 100644 index d543d1185a..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLBooleanClauseTree.java +++ /dev/null @@ -1,1429 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Internal use only. A tree structure used to evaluate a SQL boolean clause for a given result set. - */ -@Deprecated -class SQLBooleanClauseTree -{ - // Pattern match types - /** - * Indicates if it is a comparison with the beginning of a string. - */ - static final int PAT_MATCH_STARTS_WITH = 1; - - /** - * Indicates if it is a comparison with the end of a string. - */ - static final int PAT_MATCH_ENDS_WITH = 2; - - /** - * Indicates if there are two '%' symbols to compare if a string contains another. - */ - static final int PAT_MATCH_CONTAINS = 3; - - /** - * Indicates if the string to be matched can be any string. - */ - static final int PAT_MATCH_ANYTHING = 4; - - // rnovais@568_1: accept % on the middle. - /** - * Indicates if there is a match character in the middle of the string to be matched. - */ - static final int PAT_MATCH_MIDDLE = 5; - - // rnovais@_568_1: accept like without %. - /** - * Indicates if the comparison test is just a equality. - */ - static final int PAT_MATCH_EQUAL = 6; - - /** - * Pattern matching character. - */ - static final int PAT_MATCH_CHAR_ZERO_MORE = '%'; - - /** - * Tree operand type. - */ - int operandType; - - /** - * The operand name hash code. - */ - int nameHashCode; - - /** - * The operand name hash code used only for sql functions. - */ - int nameSqlFunctionHashCode; // rnovais@570_108 - - /** - * The value data type. - */ - int valueType = SQLElement.UNDEFINED; - - /** - * Pattern matching type. - */ - int patternMatchType; - - /** - * The associated table column index of the operand. - */ - int colIndex; - - /** - * Position of the % in the string. - */ - private int posPercent; // rnovais@_568_1 - - /** - * The index of the correspondent result set. - */ - int indexRs = -1; - - /** - * Indicate if the value type is a floating point type. - */ - private boolean isFloatingPointType; - - /** - * Indicates if this is a parameter. - */ - boolean isParameter; - - /** - * Indicates if the parameter value is defined. - */ - boolean isParamValueDefined; - - /** - * Indicates if the left and right tree are identifiers. - */ - boolean bothAreIdentifier; - - /** - * Indicates if it has an associated index. Used on join table1.field1 = table2.field2. - */ - boolean hasIndex; - - /** - * Tree operand name. - */ - String operandName; - - /** - * String to do the pattern match. - */ - String strToMatch; - - // Subtrees - /** - * The left tree. - */ - SQLBooleanClauseTree leftTree; - - /** - * The right tree. - */ - SQLBooleanClauseTree rightTree; - - /** - * The parent tree. - */ - SQLBooleanClauseTree parent; - - /** - * The associated SQLBooleanClause. - */ - SQLBooleanClause booleanClause; - - /** - * Tree operand value. - */ - SQLValue operandValue; - - /** - * The current value. Used only on joins comparing table fields like table1.field1 table2.field2. - */ - SQLValue valueJoin; - - /** - * A temporary SQLValue for getOperandValue() - */ - private SQLValue tempValue = new SQLValue(); - - /** - * Creates a SQLBooleanClauseTree with the associated SQLBooleanClause. - * - * @param booleanClause The associated SQLBooleanClause. - */ - SQLBooleanClauseTree(SQLBooleanClause aBooleanClause) - { - booleanClause = aBooleanClause; - } - - /** - * Sets the tree operand as a string literal. - * - * @param value The string literal value. - */ - void setOperandStringLiteral(String value) // juliana@201_34 - { - valueType = SQLElement.CHARS; - if (operandValue == null) - operandValue = new SQLValue(); - operandValue.asString = value; - setPatternMatchType(); // Checks the pattern match type. - } - - /** - * Sets a short parameter value. - * - * @param val The short value to be set. - * @throws DriverException If the parameter is not of type short. - */ - void setParamValue(short val) throws DriverException // juliana@201_34 - { - createValueAndTestType(SQLElement.SHORT); - operandValue.asShort = val; - } - - /** - * Sets a int parameter value. - * - * @param val The integer value to be set. - * @throws DriverException If the parameter is not of type int. - */ - void setParamValue(int val) throws DriverException // juliana@201_34 - { - createValueAndTestType(SQLElement.INT); - operandValue.asInt = val; - } - - /** - * Sets a long parameter value. - * - * @param val The long value to be set. - * @throws DriverException If the parameter is not of type long. - */ - void setParamValue(long val) throws DriverException // juliana@201_34 - { - createValueAndTestType(SQLElement.LONG); - operandValue.asLong = val; - } - - /** - * Sets a float parameter value. - * - * @param val The float value to be set. - * @throws DriverException If the parameter is not of type float. - */ - void setParamValue(float val) throws DriverException // juliana@201_34 - { - createValueAndTestType(SQLElement.FLOAT); - operandValue.asDouble = val; - } - - /** - * Sets a double parameter value. - * - * @param val The double value to be set. - * @throws DriverException If the parameter is not of type double. - */ - void setParamValue(double val) throws DriverException // juliana@201_34 - { - createValueAndTestType(SQLElement.DOUBLE); - operandValue.asDouble = val; - } - - // juliana@201_34: setString() for a where clause wouldn't work if the table column wasn't a date, datetime, chars or chars nocase. - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. - /** - * Sets a string parameter value. - * - * @param val The string value to be set. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - * @throws DriverException If a blob is set as a string. - */ - void setParamValue(String val) throws InvalidDateException, InvalidNumberException - { - SQLValue value = operandValue; - - isParamValueDefined = true; - if (value == null) - value = operandValue = new SQLValue(); - value.asString = val; - - switch (valueType) - { - case SQLElement.SHORT: - value.asShort = Convert.toShort(val); - break; - - case SQLElement.INT: - value.asInt = Convert.toInt(val); - break; - - case SQLElement.LONG: - operandValue.asLong = Convert.toLong(val); - break; - - case SQLElement.FLOAT: - value.asDouble = Utils.toFloat(val); - break; - - case SQLElement.DOUBLE: - value.asDouble = Convert.toDouble(val); - break; - - case SQLElement.DATE: // rnovais@570_55: If the type is DATE, checks if it is valid and converts it to int. - value.asInt = booleanClause.tempDate.set(val.trim(), Settings.DATE_YMD); - break; - - case SQLElement.DATETIME: // If the type is DATETIME, checks if it is valid and converts it to 2 ints. - val = val.trim(); - int pos = val.lastIndexOf(' '); - if (pos == -1) // If it has only a date... - { - value.asInt = booleanClause.tempDate.set(val, Settings.DATE_YMD); // Gets the date part. - value.asShort = 0; - } - else - { - value.asInt = booleanClause.tempDate.set(val.substring(0, pos), Settings.DATE_YMD); // Gets the date part. - value.asShort = Utils.testAndPrepareTime(val.substring(pos + 1)); // Gets the time part. - } - break; - - case SQLElement.UNDEFINED: // If the type is not defined, it is CHARS. - valueType = SQLElement.CHARS; - } - - setPatternMatchType(); // Checks the pattern match type. - } - - /** - * Checks the if the operand value string contains pattern matching characters and assigns the proper matching type. - */ - private void setPatternMatchType() - { - String value = operandValue.asString; - - // Checks if there is any pattern matching. - int len = value.length(); - if (len > 0) // guich@512_5 - { - char firstChar = value.charAt(0); - char lastChar = value.charAt(len - 1); - - if (firstChar == PAT_MATCH_CHAR_ZERO_MORE) // '%...' - { - if (len == 1) // '%' // juliana@230_1: solved a bug with LIKE "%". - { - patternMatchType = PAT_MATCH_ANYTHING; - strToMatch = ""; - } - else - if (lastChar == PAT_MATCH_CHAR_ZERO_MORE) // '%...%' - { - patternMatchType = PAT_MATCH_CONTAINS; - strToMatch = value.substring(1, len - 1); - } - else // '%...' - { - patternMatchType = PAT_MATCH_ENDS_WITH; - strToMatch = value.substring(1, len); - } - } - else - if (lastChar == PAT_MATCH_CHAR_ZERO_MORE) // '...%' - { - patternMatchType = PAT_MATCH_STARTS_WITH; - strToMatch = value.substring(0, len - 1); - } - else // rnovais@568_1: accepts without % or % in the middle. - { - int pos = value.indexOf('%'); - if (pos > 0) // In the middle. - { - patternMatchType = PAT_MATCH_MIDDLE; - strToMatch = value; - posPercent = pos; - } - else // Without %. - { - patternMatchType = PAT_MATCH_EQUAL; - strToMatch = value; - } - } - } - } - - // juliana@238_2: improved join table reordering. - /** - * Weighs the tree to order the table on join operation. - */ - void weightTheTree() - { - switch (operandType) // Checks the type of the operand. - { - case SQLElement.OP_BOOLEAN_AND: - case SQLElement.OP_BOOLEAN_OR: - // juliana@214_4: nots were removed. - if (leftTree != null) - leftTree.weightTheTree(); - if (rightTree != null) - rightTree.weightTheTree(); - break; - - default: // The others. - SQLBooleanClauseTree left = leftTree, - right = rightTree; - SQLBooleanClause booleanClauseAux = booleanClause; - SQLResultSetField leftField = booleanClauseAux.fieldList[booleanClauseAux.fieldName2Index.get(left.nameSqlFunctionHashCode, 0)], - rightField = booleanClauseAux.fieldList[booleanClauseAux.fieldName2Index.get(right.nameSqlFunctionHashCode, 0)]; - Index leftIndex = leftField.table.columnIndices[leftField.tableColIndex], - rightIndex = rightField.table.columnIndices[rightField.tableColIndex]; - - // field.indexRs is filled on the where clause validation. Both are identifiers. - if (left.operandType == SQLElement.OP_IDENTIFIER && right.operandType == SQLElement.OP_IDENTIFIER) - { - if (leftIndex != null) - leftField.table.weight++; - if (rightIndex != null) - rightField.table.weight++; - } - else if (left.operandType == SQLElement.OP_IDENTIFIER) - { - if (leftIndex != null) - leftField.table.weight++; - } - else if (right.operandType == SQLElement.OP_IDENTIFIER) - { - if (rightIndex != null) - rightField.table.weight++; - } - } - } - - /** - * Prepares the tree for the join operation. - */ - void setIndexRsOnTree() - { - switch (operandType) // Checks the operand type. - { - case SQLElement.OP_BOOLEAN_AND: - case SQLElement.OP_BOOLEAN_OR: - // juliana@214_4: nots were removed. - indexRs = -1; - if (leftTree != null) // Sets the indexRs on the left tree. - leftTree.setIndexRsOnTree(); - if (rightTree != null) // Sets the indexRs on the right tree. - rightTree.setIndexRsOnTree(); - break; - case SQLElement.OP_PAT_MATCH_LIKE: - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - case SQLElement.OP_PAT_IS: - case SQLElement.OP_PAT_IS_NOT: - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - case SQLElement.OP_REL_LESS_EQUAL: - case SQLElement.OP_REL_DIFF: - SQLBooleanClauseTree left = leftTree, - right = rightTree; - SQLBooleanClause clause = booleanClause; - - // field.indexRs is filled on the where clause validation. Both are identifier. - if (left.operandType == SQLElement.OP_IDENTIFIER || right.operandType == SQLElement.OP_IDENTIFIER) - { - int lIdx = clause.fieldName2Index.get(left.nameSqlFunctionHashCode != 0? left.nameSqlFunctionHashCode - : left.nameHashCode, -1), - rIdx = clause.fieldName2Index.get(right.nameSqlFunctionHashCode != 0? right.nameSqlFunctionHashCode - : right.nameHashCode, -1); - if (left.operandType == SQLElement.OP_IDENTIFIER && right.operandType == SQLElement.OP_IDENTIFIER) - { - // Puts the highest index on the indexRs. - int leftIndex = left.indexRs = clause.fieldList[lIdx].indexRs; - int rightIndex = right.indexRs = clause.fieldList[rIdx].indexRs; - - if (leftIndex <= rightIndex) // Puts the least index on the left. - indexRs = rightIndex; - else - { - SQLBooleanClauseTree auxTree = left; - leftTree = right; - rightTree = auxTree; - indexRs = leftIndex; - } - - // juliana@263_2: corrected a very old bug in a join with comparision between two fields of the same table. - if (leftIndex != rightIndex) - bothAreIdentifier = true; - } - else - indexRs = clause.fieldList[left.operandType == SQLElement.OP_IDENTIFIER ? lIdx : rIdx].indexRs; - } - } - } - - /** - * Used for composed indices to find some properties related to a branch of the expression tree. - * - * @param columns The columns of the expression tree. - * @param operators The operators of the expression tree. - * @param indexesValueTree The part of the tree that uses indices. - * @param pos The index of branch being analyzed. - */ - void getBranchProperties(byte[] columns, byte[] operators, SQLBooleanClauseTree[] indexesValueTree, int pos) - { - if (pos >= columns.length) // Does not let an OutOfBoundsException. - return; - - // One of the elements of the branch must be an identifier. - if (leftTree.operandType == SQLElement.OP_IDENTIFIER) - { - columns[pos] = (byte)leftTree.colIndex; - indexesValueTree[pos] = rightTree; - } - else - if (rightTree.operandType == SQLElement.OP_IDENTIFIER) - { - columns[pos] = (byte)rightTree.colIndex; - indexesValueTree[pos] = leftTree; - } - else - { - operators[pos] = -1; - return; - } - operators[pos] = (byte)operandType; - } - - /** - * Gets a value from a result set and applies a sql function if there is one to be applied. - * - * @return The value returned from the result set or the result of a function on a value of the result set. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - SQLValue getOperandValue() throws IOException, InvalidDateException - { - SQLValue v; // rnovais@568_10 - - // If the operand type is identifier, gets the value from the current row in the result set. Otherwise, just returns the tree operand value. - if (operandType == SQLElement.OP_IDENTIFIER) - { - SQLBooleanClause clause = booleanClause; - Table table = clause.resultSet.table; - table.readNullBytesOfRecord(0, false, 0); - if ((table.columnNulls[0][colIndex >> 3] & (1 << (colIndex & 7))) != 0) // There is a null value. - return null; - - // guich@tc100b4: replaced "new SQLValue" by tree.tempValue to avoid allocating this (+ 5000 times in AllTests). - v = clause.resultSet.sqlwhereclausetreeGetTableColValue(colIndex, tempValue); - - // rnovais@568_10: applies data type function. - SQLResultSetField field = clause.fieldList[clause.fieldName2Index.get(nameSqlFunctionHashCode, -1)]; // rnovais@570_108 - if (field.sqlFunction != -1) // has a data type function - v.applyDataTypeFunction(field.sqlFunction, field.parameter.dataType); - return v; - } - return operandValue; - } - - /** - * Checks if an operand is null. - * - * @return true if the operand is null and a null value is being searched or vice-versa; false, otherwise. - * @throws IOException If an internal method throws it. - */ - boolean compareNullOperands() throws IOException - { - Table table = leftTree.booleanClause.resultSet.table; - table.readNullBytesOfRecord(0, false, 0); - boolean isNull = (table.columnNulls[0][leftTree.colIndex >> 3] & (1 << (leftTree.colIndex & 7))) != 0; - return (operandType == SQLElement.OP_PAT_IS) ? isNull : !isNull; - } - - /** - * Compares two numerical operands. - * - * @return The evaluation of the comparison expression. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If number convertion fails. - */ - boolean compareNumericOperands() throws IOException, InvalidDateException, InvalidNumberException - { - boolean result = false; - SQLBooleanClauseTree left = leftTree, - right = rightTree; - SQLValue leftValue = bothAreIdentifier? left.valueJoin : left.getOperandValue(), - rightValue = right.getOperandValue(); - - if (leftValue == null || rightValue == null) // One of the values is a null value. - return false; - - int leftValueType = left.valueType, - rightValueType = right.valueType, - assignType = isFloatingPointType? SQLElement.DOUBLE : leftValueType != SQLElement.LONG && rightValueType != SQLElement.LONG - && leftValueType != SQLElement.DATETIME ? SQLElement.INT : SQLElement.LONG, - compareType = leftValueType == SQLElement.DATETIME ? SQLElement.DATETIME : assignType, - leftValueAsInt = 0, - rightValueAsInt = 0, - leftValueAsTime = 0, - rightValueAsTime = 0; - long leftValueAsLong = 0L, - rightValueAsLong = 0L; - double leftValueAsDouble = 0, - rightValueAsDouble = 0; - - switch (leftValueType) // Getting left value. - { - case SQLElement.SHORT: - switch (assignType) - { - case SQLElement.DOUBLE: - leftValueAsDouble = leftValue.asShort; - break; - case SQLElement.INT: - leftValueAsInt = leftValue.asShort; - break; - case SQLElement.LONG: - leftValueAsLong = leftValue.asShort; - } - break; - - case SQLElement.DATE: // rnovais@567_2 - case SQLElement.INT: - switch (assignType) - { - case SQLElement.DOUBLE: - leftValueAsDouble = leftValue.asInt; - break; - case SQLElement.INT: - leftValueAsInt = leftValue.asInt; - break; - case SQLElement.LONG: - leftValueAsLong = leftValue.asInt; - } - break; - - case SQLElement.LONG: - switch (assignType) - { - case SQLElement.DOUBLE: - leftValueAsDouble = (double)leftValue.asLong; - break; - case SQLElement.LONG: - leftValueAsLong = leftValue.asLong; - } - break; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - leftValueAsDouble = leftValue.asDouble; - break; - - case SQLElement.DATETIME: // rnovais@567_2 - leftValueAsInt = leftValue.asInt; - leftValueAsTime = leftValue.asShort; - } - - switch (rightValueType) // Getting right value. - { - case SQLElement.SHORT: - switch (assignType) - { - case SQLElement.DOUBLE: - rightValueAsDouble = rightValue.asShort; - break; - case SQLElement.INT: - rightValueAsInt = rightValue.asShort; - break; - case SQLElement.LONG: - rightValueAsLong = rightValue.asShort; - } - break; - - case SQLElement.INT: - switch (assignType) - { - case SQLElement.DOUBLE: - rightValueAsDouble = rightValue.asInt; - break; - case SQLElement.INT: - rightValueAsInt = rightValue.asInt; - break; - case SQLElement.LONG: - rightValueAsLong = rightValue.asInt; - } - break; - - case SQLElement.LONG: - switch (assignType) - { - case SQLElement.DOUBLE: - rightValueAsDouble = (double)rightValue.asLong; - break; - case SQLElement.LONG: - rightValueAsLong = rightValue.asLong; - } - break; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - rightValueAsDouble = rightValue.asDouble; - break; - - case SQLElement.DATE: // rnovais@570_55 - case SQLElement.DATETIME:// rnovais@570_55 - case SQLElement.CHARS: // rnovais@567_2 : this is for DATE and DATETIME typed that are CHARS type in the sql. - if (leftValueType == SQLElement.DATETIME) - { - rightValueAsInt = rightValue.asInt; - rightValueAsTime = rightValue.asShort; - } - else - rightValueAsInt = rightValue.asInt; - } - - if (leftValueType == -1 || rightValueType == -1) // Prevents problems when doing comparisons like 1 == 2. - { - String leftValueAsString = leftValue.asString, - rightValueAsString = rightValue.asString; - int last = leftValueAsString.length() - 1; - char lastChar = leftValueAsString.charAt(last); - - if (lastChar == 'l' || lastChar == 'L') // Strips off the final 'L', because toDouble() does not like it. - leftValueAsString = leftValueAsString.substring(0, last); - last = rightValueAsString.length() - 1; - lastChar = rightValueAsString.charAt(last); - if (lastChar == 'l' || lastChar == 'L') - rightValueAsString = rightValueAsString.substring(0, last); - leftValue.asDouble = leftValueAsDouble = Convert.toDouble(leftValueAsString); - rightValue.asDouble = rightValueAsDouble = Convert.toDouble(rightValueAsString); - left.valueType = right.valueType = SQLElement.DOUBLE; - isFloatingPointType = true; - - compareType = SQLElement.DOUBLE; // juliana@225_12: a numeric constant in a boolean clause must have its type considered to be double. - } - - switch (operandType) // Then performs the comparison. - { - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_DIFF: - switch (compareType) - { - case SQLElement.DOUBLE: - result = (Convert.doubleToLongBits(leftValueAsDouble) == Convert.doubleToLongBits(rightValueAsDouble)); - break; - case SQLElement.INT: - result = (leftValueAsInt == rightValueAsInt); - break; - case SQLElement.LONG: - result = (leftValueAsLong == rightValueAsLong); - break; - case SQLElement.DATETIME: - result = (leftValueAsInt == rightValueAsInt) && (leftValueAsTime == rightValueAsTime); - } - if (operandType == SQLElement.OP_REL_DIFF) - result = !result; - break; - - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_LESS_EQUAL: - switch (compareType) - { - case SQLElement.DOUBLE: - result = (leftValueAsDouble > rightValueAsDouble); - break; - case SQLElement.INT: - result = (leftValueAsInt > rightValueAsInt); - break; - case SQLElement.LONG: - result = (leftValueAsLong > rightValueAsLong); - break; - case SQLElement.DATETIME: - result = (leftValueAsInt == rightValueAsInt)? (leftValueAsTime > rightValueAsTime): (leftValueAsInt > rightValueAsInt); - } - if (operandType == SQLElement.OP_REL_LESS_EQUAL) - result = !result; - break; - - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_GREATER_EQUAL: - switch (compareType) - { - case SQLElement.DOUBLE: - result = (leftValueAsDouble < rightValueAsDouble); - break; - case SQLElement.INT: - result = (leftValueAsInt < rightValueAsInt); - break; - case SQLElement.LONG: - result = (leftValueAsLong < rightValueAsLong); - break; - case SQLElement.DATETIME: - result = (leftValueAsInt == rightValueAsInt)? (leftValueAsTime < rightValueAsTime): (leftValueAsInt < rightValueAsInt); - } - if (operandType == SQLElement.OP_REL_GREATER_EQUAL) - result = !result; - break; - - default: - result = false; - } - return result; - } - - /** - * Compares two strings using LIKE and NOT LIKE. - * - * @param ignoreCase Indicates if both strings are CHARS_NOCASE. - * @return The evaluation of the comparison expression. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - boolean matchStringOperands(boolean ignoreCase) throws IOException, InvalidDateException - { - boolean result = false; - SQLBooleanClauseTree right = rightTree; - SQLValue leftValue = leftTree.getOperandValue(); - - if (leftValue == null) // null value - return false; - - String leftString = leftValue.asString, - strToMatch = right.strToMatch; - - // juliana@230_3: corrected a bug of LIKE using DATE and DATETIME not returning the correct result. - leftString = Utils.formatDateDateTime(booleanClause.resultSet.table.db.driver.sBuffer, leftTree.valueType, leftValue); - - if (ignoreCase) - { - leftString = leftString.toLowerCase(); - if (strToMatch != null) // juliana@220_9: solved a NPE when using like '%' with a nocase string column. - strToMatch = strToMatch.toLowerCase(); - } - - switch (right.patternMatchType) - { - case SQLBooleanClauseTree.PAT_MATCH_ANYTHING: - result = true; - break; - - case SQLBooleanClauseTree.PAT_MATCH_STARTS_WITH: - result = leftString.startsWith(strToMatch); - break; - - case SQLBooleanClauseTree.PAT_MATCH_ENDS_WITH: - result = leftString.endsWith(strToMatch); - break; - - case SQLBooleanClauseTree.PAT_MATCH_CONTAINS: - result = leftString.indexOf(strToMatch) >= 0; - break; - - case SQLBooleanClauseTree.PAT_MATCH_MIDDLE: // rnovais@568_1 - int pos = right.posPercent; - result = leftString.startsWith(strToMatch.substring(0, pos))? (leftString.endsWith(strToMatch.substring(pos + 1))) : false; - break; - - case SQLBooleanClauseTree.PAT_MATCH_EQUAL: // rnovais@_568_1 - result = leftString.equals(strToMatch); - } - - if (operandType == SQLElement.OP_PAT_MATCH_NOT_LIKE) - result = !result; - - return result; - } - - /** - * Normal comparison between two strings. - * - * @param ignoreCase Indicates if both strings are CHARS_NOCASE. - * @return The evaluation of the comparison expression. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - boolean compareStringOperands(boolean ignoreCase) throws IOException, InvalidDateException - { - int result; - - SQLValue leftValue = bothAreIdentifier? leftTree.valueJoin : leftTree.getOperandValue(), - rightValue = rightTree.getOperandValue(); - - if (leftValue == null || rightValue == null) // null value - return false; - - String leftString = leftValue.asString, - rightString = rightValue.asString; - - if (ignoreCase) - result = leftString.toLowerCase().compareTo(rightString.toLowerCase()); - else - result = leftString.compareTo(rightString); - - switch (operandType) - { - case SQLElement.OP_REL_LESS: - return result < 0; - - case SQLElement.OP_REL_EQUAL: - return result == 0; - - case SQLElement.OP_REL_GREATER: - return result > 0; - - case SQLElement.OP_REL_GREATER_EQUAL: - return result >= 0; - - case SQLElement.OP_REL_LESS_EQUAL: - return result <= 0; - - case SQLElement.OP_REL_DIFF: - return result != 0; - } - - return false; - } - - /** - * Evaluates an expression tree. - * - * @return The value of the expression evaluation. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - boolean booleanTreeEvaluate() throws IOException, InvalidDateException, InvalidNumberException - { - boolean result = false, - leftResult = false, - rightResult = false; - - switch (operandType) // Checks the operand type of the tree. - { - // Relational operantors. - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - case SQLElement.OP_REL_LESS_EQUAL: - case SQLElement.OP_REL_DIFF: - switch (valueType) // Calls the right operation accordingly to the values type. - { - case SQLElement.SHORT: - case SQLElement.INT: - case SQLElement.LONG: - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - case SQLElement.DATE: // rnovais@567_2 - case SQLElement.DATETIME: // rnovais@567_2 - result = compareNumericOperands(); - break; - - case SQLElement.CHARS: - result = compareStringOperands(false); - break; - - case SQLElement.CHARS_NOCASE: - result = compareStringOperands(true); - break; - } - break; - - // LIKE operators. - case SQLElement.OP_PAT_MATCH_LIKE: - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - result = matchStringOperands(valueType == SQLElement.CHARS_NOCASE); - break; - - case SQLElement.OP_BOOLEAN_AND: // AND connector. - if (leftTree != null && rightTree != null) - { - leftResult = leftTree.booleanTreeEvaluate(); - - if (leftResult) // Short circuit: only evaluates the right tree if left result is TRUE. - { - rightResult = rightTree.booleanTreeEvaluate(); - result = leftResult && rightResult; - } - } - break; - - case SQLElement.OP_BOOLEAN_OR: // OR connector. - if (leftTree != null && rightTree != null) // Expects both trees to be not null. - { - leftResult = leftTree.booleanTreeEvaluate(); - - if (leftResult) // Short circuit: only evaluates the right tree if the left result is FALSE. - result = true; - else - { - rightResult = rightTree.booleanTreeEvaluate(); - result = leftResult || rightResult; - } - } - break; - - // juliana@214_4: nots were removed. - - // IS operators. - case SQLElement.OP_PAT_IS: - case SQLElement.OP_PAT_IS_NOT: - result = compareNullOperands(); - } - - return result; - } - - /** - * Binds the column information of the underlying table to the boolean clause tree nodes. - * - * @throws SQLParseException If a column can not be bound. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - void bindColumnsSQLBooleanClauseTree() throws SQLParseException, InvalidDateException, InvalidNumberException - { - if (operandType == SQLElement.OP_IDENTIFIER) // If operand type is identifier, binds to a column in the table. - { - int dtParameter; // rnovais@568_10 - - // Stores the binding information also in the boolean clause field list. - int fieldIndex = booleanClause.fieldName2Index.get(nameSqlFunctionHashCode, -1); - - if (fieldIndex == -1) // The column could not be found. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + operandName); - - SQLResultSetField field = booleanClause.fieldList[fieldIndex]; - colIndex = field.tableColIndex; - - if (colIndex < 0) // The column could not be found. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + operandName); - - dtParameter = field.dataType; - if (valueType != SQLElement.UNDEFINED && valueType != dtParameter) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - if (field.sqlFunction == -1) // rnovais@568_10: if it does not have a data function, stores the correct value type. - valueType = dtParameter; - else - { - if (field.dataType == SQLElement.UNDEFINED) // rnovais@570_5 - dtParameter = field.dataType = field.parameter.dataType; - valueType = dtParameter; - } - } - - SQLBooleanClauseTree left = leftTree, - right = rightTree; - - // Bind the columns of the children trees. - if (left != null) - left.bindColumnsSQLBooleanClauseTree(); - if (right != null) - right.bindColumnsSQLBooleanClauseTree(); - - if (left != null && right != null) // Infers the operation resulting value type. - inferOperationValueType(); - - // rnovais@567_2: validates date and datetime in the rightTree. - if ((left != null) && right.operandValue != null - && (left.valueType == SQLElement.DATE || left.valueType == SQLElement.DATETIME)) - right.operandValue.validateDateTime(booleanClause.tempDate, left.valueType); - } - - /** - * Infers the operation value type, according to the left and right values involved in the operation. - * - * @throws SQLParseException If left and right values have incompatible types or there is a blob type in the comparison. - * @throws InvalidNumberException If an internal method throws it. - */ - void inferOperationValueType() throws SQLParseException, InvalidNumberException - { - SQLBooleanClauseTree left = leftTree, - right = rightTree; - - if (left == null || right == null) - { - valueType = SQLElement.UNDEFINED; - return; - } - if (right.operandType == SQLElement.OP_PAT_NULL) - { - valueType = left.valueType; - return; - } - - if (left.operandName != null) // rnovais@568_10: if it has a data type function, verifies if it can be applied. - { - SQLResultSetField field = left.booleanClause.fieldList[left.booleanClause.fieldName2Index.get(left.nameSqlFunctionHashCode == 0? - left.nameHashCode : left.nameSqlFunctionHashCode, -1)]; - if (field.sqlFunction != -1) - Utils.bindFunctionDataType(field.parameter.dataType, field.sqlFunction); - } - - if (operandType == SQLElement.OP_BOOLEAN_AND || operandType == SQLElement.OP_BOOLEAN_OR) // Boolean type. - { - valueType = SQLElement.BOOLEAN; - return; - } - - int leftOperandType = left.operandType, - rightOperandType = right.operandType, - leftValueType = left.valueType, - rightValueType = right.valueType; - - if (leftValueType == SQLElement.BLOB || rightValueType == SQLElement.BLOB) // Blobs can't be compared. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COMP_BLOBS)); - - boolean leftIsParameter = left.isParameter, - rightIsParameter = right.isParameter; - - // In case one of them is a parameter, the tree has the type of the one that is not a parameter. - // If both are parameters, the type is undefined (which is the default, anyway). - if (leftIsParameter || rightIsParameter) - { - if (leftIsParameter) - { - left.valueType = rightValueType; - valueType = rightValueType; - } - else - { - right.valueType = leftValueType; - valueType = leftValueType; - } - } - else - { - // juliana@201_12: both should be compared to DATE and DATETIME. - boolean leftIsChar = (leftValueType == SQLElement.CHARS || leftValueType == SQLElement.CHARS_NOCASE || leftValueType == SQLElement.DATE - || leftValueType == SQLElement.DATETIME), - rightIsChar = (rightValueType == SQLElement.CHARS || rightValueType == SQLElement.CHARS_NOCASE || leftValueType == SQLElement.DATE - || leftValueType == SQLElement.DATETIME); - - if (leftIsChar != rightIsChar) // Can not mix a character type with a non-character type, except for DATE and DATETIME. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - // If one of the operands is an identifier and the other one is not, it prevails the identifier operand value type. - if (rightOperandType == SQLElement.OP_IDENTIFIER && leftOperandType != SQLElement.OP_IDENTIFIER) - { - valueType = rightValueType; - left.convertValue(rightValueType); - } - else - if (leftOperandType == SQLElement.OP_IDENTIFIER && rightOperandType != SQLElement.OP_IDENTIFIER) - { - valueType = leftValueType; - right.convertValue(leftValueType); - } - else - switch (operandType) - { - // Relational operators. - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - case SQLElement.OP_REL_LESS_EQUAL: - case SQLElement.OP_REL_DIFF: - if (leftIsChar || rightIsChar) - { - if (leftValueType == SQLElement.DATE || rightValueType == SQLElement.DATE) // rnovais@567_2 - valueType = SQLElement.DATE; - else - if (leftValueType == SQLElement.DATETIME || rightValueType == SQLElement.DATETIME) - valueType = SQLElement.DATETIME; - else - { - // If both are identifiers, it prevails CHARS, in case one of them is CHARS (case sensitive). - if (leftOperandType == SQLElement.OP_IDENTIFIER && rightOperandType == SQLElement.OP_IDENTIFIER) - valueType = (leftValueType == SQLElement.CHARS || rightValueType == SQLElement.CHARS)? SQLElement.CHARS - : SQLElement.CHARS_NOCASE; - else - valueType = SQLElement.CHARS; - } - } - else - { - // This order is important. - if (leftValueType == SQLElement.DOUBLE || rightValueType == SQLElement.DOUBLE) - valueType = SQLElement.DOUBLE; - else - if (leftValueType == SQLElement.FLOAT || rightValueType == SQLElement.FLOAT) - valueType = SQLElement.FLOAT; - else - if (leftValueType == SQLElement.LONG || rightValueType == SQLElement.LONG) - valueType = SQLElement.LONG; - else - if (leftValueType == SQLElement.INT || rightValueType == SQLElement.INT) - valueType = SQLElement.INT; - else - valueType = SQLElement.SHORT; - } - break; - - // Like operators. - case SQLElement.OP_PAT_MATCH_LIKE: - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - if (!leftIsChar || !rightIsChar) // Only character types are allowed here. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - if (leftValueType == SQLElement.DATE || rightValueType == SQLElement.DATE) // rnovais@567_2 - valueType = SQLElement.DATE; - else - if (leftValueType == SQLElement.DATETIME || rightValueType == SQLElement.DATETIME) - valueType = SQLElement.DATETIME; - else - { - // If both are identifiers, it prevails CHARS, in case one of them is CHARS (case sensitive). - if (leftOperandType == SQLElement.OP_IDENTIFIER && rightOperandType == SQLElement.OP_IDENTIFIER) - { - if (leftValueType == SQLElement.CHARS || rightValueType == SQLElement.CHARS) - valueType = SQLElement.CHARS; - else - valueType = SQLElement.CHARS_NOCASE; - } - else - valueType = SQLElement.CHARS; - } - break; - - default: - valueType = SQLElement.UNDEFINED; - } - } - - // Checks if the operand value type has floating point. - isFloatingPointType = (valueType == SQLElement.DOUBLE || valueType == SQLElement.FLOAT); - } - - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. - /** - * Converts a number in the string format to its numerical representation. - * - * @param type The number type. - * @throws InvalidNumberException If the conversion fails. - */ - void convertValue(int type) throws InvalidNumberException - { - switch (type) - { - case SQLElement.SHORT: - operandValue.asShort = Convert.toShort(operandValue.asString); - break; - case SQLElement.INT: - operandValue.asInt = Convert.toInt(operandValue.asString); - break; - case SQLElement.LONG: - operandValue.asLong = Convert.toLong(operandValue.asString); - break; - case SQLElement.FLOAT: - operandValue.asDouble = Utils.toFloat(operandValue.asString); - break; - case SQLElement.DOUBLE: - operandValue.asDouble = Convert.toDouble(operandValue.asString); - break; - default: - return; - } - valueType = type; - } - - // juliana@214_4: removes not from expression trees so that indices can be used in more situations. - /** - * Removes the not operators from an expression tree. - * - * @param expressionTree The expression tree passed by the user. - * @return The expression tree without nots. - */ - static SQLBooleanClauseTree removeNots(SQLBooleanClauseTree expressionTree) - { - if (expressionTree == null) // Does nothing with an empty expression is used. - return null; - - SQLBooleanClauseTree right = expressionTree.rightTree; - - if (expressionTree.operandType == SQLElement.OP_BOOLEAN_NOT) - { - switch (right.operandType) - { - case SQLElement.OP_BOOLEAN_AND: // not (A and B) == not A or not B. - SQLBooleanClauseTree tree = new SQLBooleanClauseTree(expressionTree.booleanClause); - tree.operandType = SQLElement.OP_BOOLEAN_OR; - tree.leftTree = expressionTree; - tree.rightTree = right; - tree.rightTree.operandType = SQLElement.OP_BOOLEAN_NOT; - expressionTree.parent = tree.rightTree.parent = tree; - right = expressionTree.rightTree = tree.rightTree.leftTree; - tree.rightTree.leftTree = null; - right.parent = expressionTree; - expressionTree = tree; - break; - case SQLElement.OP_BOOLEAN_OR: // not (A or B) == not A and not B. - tree = new SQLBooleanClauseTree(expressionTree.booleanClause); - tree.operandType = SQLElement.OP_BOOLEAN_AND; - tree.leftTree = expressionTree; - tree.rightTree = right; - tree.rightTree.operandType = SQLElement.OP_BOOLEAN_NOT; - expressionTree.parent = tree.rightTree.parent = tree; - right = expressionTree.rightTree = tree.rightTree.leftTree; - tree.rightTree.leftTree = null; - right.parent = expressionTree; - expressionTree = tree; - break; - case SQLElement.OP_BOOLEAN_NOT: // not not == null. - right.rightTree.parent = expressionTree.parent; - expressionTree = right.rightTree; - break; - case SQLElement.OP_REL_LESS: // not less == greater equal. - right.operandType = SQLElement.OP_REL_GREATER_EQUAL; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_REL_EQUAL: // not equal == dif. - right.operandType = SQLElement.OP_REL_DIFF; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_REL_GREATER: // not greater == less equal. - right.operandType = SQLElement.OP_REL_LESS_EQUAL; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_REL_GREATER_EQUAL: // not greater equal == less. - right.operandType = SQLElement.OP_REL_LESS; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_REL_LESS_EQUAL: // not less equal == greates. - right.operandType = SQLElement.OP_REL_GREATER; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_REL_DIFF: // not dif == equal. - right.operandType = SQLElement.OP_REL_EQUAL; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_PAT_MATCH_LIKE: // not like == not like. - right.operandType = SQLElement.OP_PAT_MATCH_NOT_LIKE; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_PAT_MATCH_NOT_LIKE: // not not like == like. - right.operandType = SQLElement.OP_PAT_MATCH_LIKE; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_PAT_IS: // not is == is not. - right.operandType = SQLElement.OP_PAT_IS_NOT; - right.parent = expressionTree.parent; - expressionTree = right; - break; - case SQLElement.OP_PAT_IS_NOT: // not is not == is. - right.operandType = SQLElement.OP_PAT_IS; - right.parent = expressionTree.parent; - expressionTree = right; - } - } - - // Recursion. - expressionTree.leftTree = removeNots(expressionTree.leftTree); - expressionTree.rightTree = removeNots(expressionTree.rightTree); - - return expressionTree; - } - - // juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first - // execution. - /** - * Clones an expression tree of a where clause of a select prepared statement. - * - * @param destTree The old destination tree. If the nodes are the same of the tree cloned, the node is reused in the cloned tree. - * @return A clone of the where clause expression tree. - */ - SQLBooleanClauseTree cloneTree(SQLBooleanClauseTree destTree) - { - SQLBooleanClause clause = booleanClause; - SQLBooleanClauseTree[] paramList = clause.paramList; - SQLBooleanClauseTree tree = null; - int i = clause.paramCount; - - while (--i >= 0) - if (this == paramList[i]) - { - tree = this; - break; - } - if (i == -1) - if (destTree != this) - { - (tree = new SQLBooleanClauseTree(clause)).booleanClause = clause; - tree.bothAreIdentifier = bothAreIdentifier; - tree.colIndex = colIndex; - tree.hasIndex = hasIndex; - tree.indexRs = indexRs; - tree.isFloatingPointType = isFloatingPointType; - tree.isParameter = isParameter; - tree.isParamValueDefined = isParamValueDefined; - tree.nameHashCode = nameHashCode; - tree.nameSqlFunctionHashCode = nameSqlFunctionHashCode; - tree.operandName = operandName; - tree.operandType = operandType; - tree.operandValue = operandValue; - tree.patternMatchType = patternMatchType; - tree.posPercent = posPercent; - tree.strToMatch = strToMatch; - tree.tempValue = tempValue; - tree.valueType = valueType; - } - else - tree = destTree; - if (leftTree != null) - (tree.leftTree = leftTree.cloneTree(destTree == null? null : destTree.leftTree)).parent = tree; - if (rightTree != null) - (tree.rightTree = rightTree.cloneTree(destTree == null? null : destTree.rightTree)).parent = tree; - return tree; - } - - /** - * Create the operand value object if necessary and tests its type. - * - * @param type The type of the operand value. - * @throws DriverException If the types are incompatible. - */ - private void createValueAndTestType(int type) - { - isParamValueDefined = true; - if (operandValue == null) - operandValue = new SQLValue(); - if (valueType == SQLElement.UNDEFINED) - valueType = type; - else if (valueType != type) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - operandValue.asString = null; - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLColumnListClause.java b/LitebaseSDK/src/java/litebase/SQLColumnListClause.java deleted file mode 100644 index cf26f5a346..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLColumnListClause.java +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.util.*; - -/** - * Internal use only. Represents a SQL column list clause, like order by or group by.
    - * Note: The select clause has a different class SQLSelectClause, since it has a different complexity. - */ -@Deprecated -class SQLColumnListClause -{ - /** - * The column field list. - */ - SQLResultSetField[] fieldList = new SQLResultSetField[SQLElement.MAX_NUM_COLUMNS]; - - /** - * The number of fields. - */ - int fieldsCount; - - /** - * Indicates the index to use when doing a sort operation. - */ - int index = -1; // juliana@230_29: order by and group by now use indices on simple queries. - - /** - * Indicates that the index to be used is composed or not. - */ - boolean isComposed; // juliana@230_29: order by and group by now use indices on simple queries. - - // juliana@266_1: corrected a possible AIOOBE when doing a order by or group by in a prepared statement in a table with more than 128 columns - // on Java and BlackBerry. - /** - * Backup for the tableColIndexes, used in prepared statements. - */ - short[] fieldTableColIndexesBak; // guich@554_37 - - /** - * Compares two SQL column lists clauses. They can only be considered equal if they list the same column list in the same sequence. - * - * @param columnListClause the SQLColumnListClause to compare against. - * @throws SQLParseException If both column lists do not list the same column sequence. - */ - public void checkEquality(SQLColumnListClause columnListClause) throws SQLParseException - { - int len = fieldsCount; // the length of the first column list. - - if (len != columnListClause.fieldsCount) // If the length of the column lists are different, the lists are different. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_ORDER_GROUPBY_MUST_MATCH)); - - SQLResultSetField[] fieldList2 = columnListClause.fieldList; - - // If a field of one list has an index of a column different of the same member of the other list or they don't match concerning the ordering - // of the result, the lists are considered to be different. - while (--len >= 0) - if (fieldList[len].tableColIndex != fieldList2[len].tableColIndex) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_ORDER_GROUPBY_MUST_MATCH)); - } - - /** - * Checks if the column list contains the given column. - * - * @param colIndex The column index of the column being searched for. - * @throws SQLParseException If the column is not in the column list clause. - */ - void sqlcolumnlistclauseContains(int colIndex) throws SQLParseException - { - SQLResultSetField[] list = fieldList; - int i = fieldsCount; - while (--i >= 0) - if (list[i].tableColIndex == colIndex) - return; - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT)); - } - - /** - * Binds the column information of the underlying order or group by clause to the select clause. - * - * @param names2Index The select clause columns hash table. - * @param columTypes The select clause tables column types. - * @param tableList The select clause tables. - * @throws SQLParseException If the column in a group or order by clause is not in the select clause or there is a column of type blob in the - * clause. - */ - void bindColumnsSQLColumnListClause(IntHashtable names2Index, byte[] columnTypes, SQLResultSetTable[] tableList) throws SQLParseException - { - int i = fieldsCount, - index; - SQLResultSetField field; - - if (tableList == null) // Bind during a sorting. - while (--i >= 0) - { - field = fieldList[i]; - field.dataType = columnTypes[field.tableColIndex = names2Index.get(field.tableColHashCode, -1)]; - } - else // Bind before executing the query. - { - int j, - n = tableList.length; - - while (--i >= 0) - { - field = fieldList[i]; - index = -1; - j = n; - while (--j >= 0) - { - index = tableList[j].table.htName2index.get(field.tableColHashCode, -1); - if (field.alias.indexOf('.') > 0 && !field.alias.startsWith(tableList[j].aliasTableName)) - index = -1; - if (index != -1) - { - field.table = tableList[j].table; // juliana@230_29 - break; - } - } - - if (index == -1) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.tableColName); - - field.dataType = columnTypes[field.tableColIndex = index]; - if (field.dataType == SQLElement.BLOB) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_ORDER_GROUP)); - } - - } - - } - - // juliana@230_29: order by and group by now use indices on simple queries. - /** - * Finds the best index to use in a sort operation. - */ - void findSortIndex() - { - int length = fieldsCount; - SQLResultSetField[] fields = fieldList; - SQLResultSetField field = fields[0]; - Table table = field.table; - - if (length == 1) - { - // To use an index for ordering, it must use only non-null columns because Litebase indices don't store nulls. - // If there is only one field and it is a primary key, uses the primary key index (it is not null). - if (table.primaryKeyCol == field.tableColIndex) - { - index = field.tableColIndex; - isComposed = false; - } - - // If it is another not null field, try to find an index for it. - else if ((table.columnAttrs[field.tableColIndex] & Utils.ATTR_COLUMN_IS_NOT_NULL) != 0) - { - field.findMaxMinIndex(); - index = field.index; - isComposed = field.isComposed; - } - } - else - { - boolean isAscending = field.isAscending, - areAllNotNull = true; - - while (--length > 0) - { - // All the fields to be sorted must have the same table and ordering. - if ((field = fields[length]).isAscending != isAscending || field.table != table) - return; - - // To use an index for ordering, it must use only non-null columns because Litebase indices don't store nulls. - if ((table.columnAttrs[field.tableColIndex] & Utils.ATTR_COLUMN_IS_NOT_NULL) == 0) - areAllNotNull = false; - } - - int i = -1; - byte[] composedPKCols = table.composedPrimaryKeyCols; - - // Checks if the fields to be sorted are the first part of the composed PK. - if (table.numberComposedPKCols >= (length = fieldsCount)) - { - while (++i < length) - if (composedPKCols[i] != fields[i].tableColIndex) - break; - - if (i == length) // If so, the composed PK can be used (it is not null). - { - index = table.composedPK; - isComposed = true; - return; - } - - } - - // If the fields to be sorted are not part of a composed PK and are not all not null, does not use an index. - if (!areAllNotNull) - return; - - // If they are all not null, it is necessary to find a composed index for them. - ComposedIndex[] compIndices = table.composedIndices; - ComposedIndex compIndex; - int j; - - i = table.numberComposedIndices; - while (--i >= 0) - { - // Checks if the fields to be sorted are the first part of the composed index. - if ((compIndex = compIndices[i]).columns.length >= length) - { - j = -1; - while (++j < length) - if (compIndex.columns[j] != fields[j].tableColIndex) - break; - - if (j == length) // If so, the composed PK can be used (it is not null). - { - index = i; - isComposed = true; - return; - } - } - } - } - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLDeleteStatement.java b/LitebaseSDK/src/java/litebase/SQLDeleteStatement.java deleted file mode 100644 index f72f9b50f7..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLDeleteStatement.java +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.InvalidDateException; - -/** - * Represents a SQL DELETE statement. - */ -@Deprecated -class SQLDeleteStatement extends SQLStatement -{ - /** - * The base table used by the SQL expression. - */ - SQLResultSetTable rsTable; - - /** - * The where clause. - */ - SQLBooleanClause whereClause; - - /** - * Constructs a delete statement given the result of the parsing process. - * - * @param parser The result of the parsing process. - */ - SQLDeleteStatement(LitebaseParser parser) - { - type = SQLElement.CMD_DELETE; - - rsTable = parser.tableList[0]; // Creates the result set table. - - SQLBooleanClause booleanClause = whereClause = parser.whereClause; // Gets the where clause. - if (booleanClause != null && booleanClause.fieldList.length != booleanClause.fieldsCount) - { - // Compacts the resulting field list. - SQLResultSetField[] compactFieldList = new SQLResultSetField[booleanClause.fieldsCount]; - Vm.arrayCopy(booleanClause.fieldList, 0, compactFieldList, 0, booleanClause.fieldsCount); - booleanClause.fieldList = compactFieldList; - - // Compacts the parameter list. - SQLBooleanClauseTree[] compactParamList = new SQLBooleanClauseTree[booleanClause.paramCount]; - Vm.arrayCopy(booleanClause.paramList, 0, compactParamList, 0, booleanClause.paramCount); - booleanClause.paramList = compactParamList; - } - } - - /** - * Sets the value of a short parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the short parameter. - */ - void setParamValue(int index, short val) - { - checkIndex(index); // Checks if the index is within the range. - whereClause.paramList[index].setParamValue(val); // Sets the value. - } - - /** - * Sets the value of an integer parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the integer parameter. - */ - void setParamValue(int index, int val) - { - checkIndex(index); // Checks if the index is within the range. - whereClause.paramList[index].setParamValue(val); // Sets the value. - } - - /** - * Sets the value of a long parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the long parameter. - * @throws DriverException If the parameter index. - */ - void setParamValue(int index, long val) - { - checkIndex(index); // Checks if the index is within the range. - whereClause.paramList[index].setParamValue(val); // Sets the value. - } - - /** - * Sets the value of a float parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the float parameter. - * @throws DriverException If the parameter index is invalid or the column parameter is not float. - */ - void setParamValue(int index, float val) - { - checkIndex(index); // Checks if the index is within the range. - whereClause.paramList[index].setParamValue(val); // Sets the value. - } - - /** - * Sets the value of a double parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the double parameter. - */ - void setParamValue(int index, double val) - { - checkIndex(index); // Checks if the index is within the range. - whereClause.paramList[index].setParamValue(val); // Sets the value. - } - - /** - * Sets the value of a string parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the string parameter. - * @throws NullPointerException If a null is used as a parameter of a where clause. - * @throws InvalidNumberException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void setParamValue(int index, String val) throws NullPointerException, InvalidNumberException, InvalidDateException - { - checkIndex(index); // Checks if the index is within the range. - if (val == null) // A null can't be in a parameter of a where clause. - throw new NullPointerException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAM_NULL)); - whereClause.paramList[index].setParamValue(val); // Sets the value. - } - - /** - * This was expected to set the value of a byte array parameter at the given index. Since blobs can't be in a where clause and a delete clause - * only has parameters in a where clause, this method will only raise an exception. - * - * @param index The index of the parameter. - * @param val The value of the blob parameter. - * @throws SQLParseException Blobs can't be in a where clause. - */ - void setParamValue(int index, byte[] val) throws SQLParseException - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_WHERE)); - } - - // juliana@223_3: PreparedStatement.setNull() now works for blobs. - /** - * This was expected to set null in a given field. Since a null can't be in a parameter of a where clause, this method will only raise an - * exception. - * - * @param index The index of the parameter. Not used since a null can't be in a parameter of a where clause. - * @throws SQLParserException A null can't be in a parameter of a where clause. - */ - void setNull(int index) throws SQLParseException - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAM_NULL)); - } - - /** - * Clears all parameter values of a prepared statement delete. - */ - void clearParamValues() - { - SQLBooleanClause booleanClause = whereClause; - if (booleanClause != null) - { - SQLBooleanClauseTree[] paramList = booleanClause.paramList; - int i = booleanClause.paramCount; - - while (--i >= 0) - paramList[i].isParamValueDefined = false; - } - } - - /** - * Checks if all parameters values are defined. - * - * @throws DriverException If not all parameter values are defined. - */ - void allParamValuesDefined() throws DriverException - { - SQLBooleanClause booleanClause = whereClause; - if (booleanClause != null) - { - SQLBooleanClauseTree[] paramList = booleanClause.paramList; - int i = booleanClause.paramCount; - - while (--i >= 0) - if (!paramList[i].isParamValueDefined) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_NOT_ALL_PARAMETERS_DEFINED)); - } - } - - /** - * Executes a delete statement. - * - * @param driver The connection with Litebase. - * @return The number of rows deleted. - * @throws DriverException If the record can't be removed from the indices. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - int litebaseDoDelete(LitebaseConnection driver) throws IOException, InvalidDateException, InvalidNumberException - { - if (rsTable.table.db.db == null) // juliana@201_28: If a table is re-created after the prepared statement is parsed, there won't be a NPE. - rsTable.table = driver.getTable(rsTable.tableName); - - Table table = rsTable.table; - PlainDB plainDB = table.db; - NormalFile dbFile = (NormalFile)plainDB.db; - ByteArrayStream bas = plainDB.bas; - DataStreamLB ds = plainDB.basds; // juliana@253_8: now Litebase supports weak cryptography. - int nn = 0, - columnCount = table.columnCount, - i = columnCount, - j; - Index index; - ComposedIndex ci; - Index[] columnIndices = table.columnIndices; - ComposedIndex[] composedIndices = table.composedIndices; - - // If there are indices, this is needed to remove the values from them. - boolean wholeTable = whereClause == null, - hasIndices = table.numberComposedIndices > 0; // juliana@201_6 - while (--i >= 0) - if (columnIndices[i] != null) - { - hasIndices = true; - break; - } - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since - // its last opening. - table.setModified(); // Sets the table as not closed properly. - - // juliana@270_32: corrected a bug of a delete not updating the number of total deleted rows in the metadata when there is an index corruption. - try - { - if (wholeTable) // Deletes the whole table. - { - if (hasIndices) // If the whole table is being deleted, just empties all indexes. - { - i = columnCount; - while (--i >= 0) - if ((index = columnIndices[i]) != null) - index.deleteAllRows(); - if ((i = table.numberComposedIndices) > 0) // juliana@201_6: it now deletes the erases the composed index when deleting the whole table. - while (--i >= 0) - composedIndices[i].index.deleteAllRows(); - } - - // juliana@227_10: Corrected a bug of a delete with no where clause not taking the already deleted rows into consideration when returning - // the number of deleted rows. - nn = plainDB.rowCount - table.deletedRowsCount; - i = table.deletedRowsCount = plainDB.rowCount; - - while (--i >= 0) - { - // Logically deletes the record: changes the attribute to 'deleted'. - plainDB.read(i); - j = (ds.readInt() & Utils.ROW_ID_MASK) | Utils.ROW_ATTR_DELETED; - bas.reset(); - ds.writeInt(j); - plainDB.rewrite(i); - } - } - else - { - // guich@300: now all records are just marked as deleted instead of physical removal. - int column; - SQLValue[] keys1 = new SQLValue[1]; - SQLValue[] keys2; - byte[] types = table.columnTypes; - short[] offsets = table.columnOffsets; - byte[] nulls = table.columnNulls[0]; - ResultSet rs = table.createSimpleResultSet(whereClause); - rs.pos = - 1; - - if (hasIndices) - while (rs.getNextRecord()) - { - i = columnCount; // juliana@201_35: Would not remove key from the index of the last column. - - // juliana@227_11: corrected a bug of an exception being thrown when trying to delete a row with a null in column which has an - // index. - while (--i >= 0) // Simple index. - if (columnIndices[i] != null && (nulls[i >> 3] & (1 << (i & 7))) == 0) - { - index = columnIndices[i]; - bas.reset(); // juliana@116_1: if reset is not done, the value read is wrong. - table.readValue(driver.sqlv, offsets[i], types[i], false, false); // juliana@220_3 juliana@230_14 - keys1[0] = driver.sqlv; - index.tempKey.set(keys1); - index.removeValue(index.tempKey, rs.pos); - } - - if ((i = table.numberComposedIndices) > 0) // Composed index. - while (--i >= 0) - { - ci = composedIndices[i]; - index = ci.index; - keys2 = SQLValue.newSQLValues(j = ci.columns.length); // juliana@201_6 - while (--j >= 0) - { - // juliana@116_1: if reset is not done, the value read is wrong. - bas.reset(); - - // juliana@220_3 - table.readValue(keys2[j], offsets[column = ci.columns[j]], types[column], false, false); // juliana@230_14 - - } - index.tempKey.set(keys2); - index.removeValue(index.tempKey, rs.pos); - } - - // Logically deletes the record: changes the attribute to 'deleted'. - i = (ds.readInt() & Utils.ROW_ID_MASK) | Utils.ROW_ATTR_DELETED; - bas.reset(); - ds.writeInt(i); - plainDB.rewrite(rs.pos); - nn++; // Increments the number of deleted rows. - } - else - while (rs.getNextRecord()) - { - // Logically deletes the record: changes the attribute to 'deleted'. - i = (ds.readInt() & Utils.ROW_ID_MASK) | Utils.ROW_ATTR_DELETED; - bas.reset(); - ds.writeInt(i); - plainDB.rewrite(rs.pos); - nn++; // Increments the number of deleted rows. - } - table.deletedRowsCount += nn; - } - return nn; - } - finally - { - if (nn > 0) - table.tableSaveMetaData(Utils.TSMD_ONLY_DELETEDROWSCOUNT); - - // juliana@227_3: improved table files flush dealing. - // juliana@270_25: corrected a possible lose of records in recover table when 10 is passed to LitebaseConnection.setRowInc(). - if (!dbFile.dontFlush) // juliana@202_23: flushs the files to disk when row increment is the default. - { - if (dbFile.cacheIsDirty) - dbFile.flushCache(); // Flushs .db. - if (((NormalFile)plainDB.dbo).cacheIsDirty) - ((NormalFile)plainDB.dbo).flushCache(); // Flushs .dbo. - } - } - } - - // nowosad@200 - /** - * Binds a SQL DELETE expression. - * - * @param driver The connection with Litebase. - * @return The delete statement binded. - * @throws InvalidDateException If it is thrown by an internal method. - * @throws InvalidNumberException If it is thrown by an internal method. - * @throws IOException If it is thrown by an internal method. - */ - SQLDeleteStatement litebaseBindDeleteStatement(LitebaseConnection driver) throws InvalidDateException, InvalidNumberException, IOException - { - Table table = driver.getTable(rsTable.tableName); - rsTable.table = table; - - if (whereClause != null) // Binds the delete statement to its table. - whereClause.bindColumnsSQLBooleanClause(table.htName2index, table.columnTypes, rsTable); - return this; - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Checks the prepared statement parameter index. - * - * @param index The index of the prepared statement parameter. - * @throws IllegalArgumentException If the index is out of range. - */ - void checkIndex(int index) throws IllegalArgumentException - { - if (index < 0 || whereClause == null || index >= whereClause.paramCount) // Checks if the index is within the range. - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLElement.java b/LitebaseSDK/src/java/litebase/SQLElement.java deleted file mode 100644 index daf4d47307..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLElement.java +++ /dev/null @@ -1,380 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Internal use only. Represents a SQL element, like data types, operands, and functions. It also has useful methods. - */ -@Deprecated -class SQLElement -{ - // Possible SQL Commands. - /** - * No command. - */ - static final int CMD_NONE = -1; - - /** - * Represents the SQL command CREATE TABLE.... - */ - static final int CMD_CREATE_TABLE = 1; - - /** - * Represents the SQL command CREATE INDEX.... - */ - static final int CMD_CREATE_INDEX = 2; - - /** - * Represents the SQL command DROP TABLE.... - */ - static final int CMD_DROP_TABLE = 3; - - /** - * Represents the SQL command DROP INDEX.... - */ - static final int CMD_DROP_INDEX = 4; - - /** - * Represents the SQL command ALTER TABLE... DROP PRIMAY KEY. - */ - static final int CMD_ALTER_DROP_PK = 5; - - /** - * Represents the SQL command ALTER TABLE... ADD PRIMAY KEY. - */ - static final int CMD_ALTER_ADD_PK = 6; - - /** - * Represents the SQL command ALTER TABLE... RENAME TO... . - */ - static final int CMD_ALTER_RENAME_TABLE = 7; - - /** - * Represents the SQL command ALTER TABLE... RENAME... TO.... - */ - static final int CMD_ALTER_RENAME_COLUMN = 8; - - /** - * Represents the SQL command ALTER TABLE ADD... (new column definition) . - */ - static final int CMD_ALTER_ADD_COLUMN = 9; // juliana@253_22: added command ALTER TABLE ADD column. - - /** - * Represents the SQL command SELECT [DISTINCT].... - */ - static final int CMD_SELECT = 10; - - /** - * Represents the SQL command INSERT INTO.... - */ - static final int CMD_INSERT = 11; - - /** - * Represents the SQL command UPDATE.... - */ - static final int CMD_UPDATE = 12; - - /** - * Represents the SQL command DELETE [FROM].... - */ - static final int CMD_DELETE = 13; - - // SQL Data Types. - /** - * Undefined type, which includes any type - */ - static final int UNDEFINED = -1; - - // The following numbers must be synchronized with ResultSetMetaData types. - /** - * String type. - */ - static final int CHARS = 0; - - /** - * Short type. - */ - static final int SHORT = 1; - - /** - * Integer type. - */ - static final int INT = 2; - - /** - * Long type. - */ - static final int LONG = 3; - - /** - * Single precision floating point number. - */ - static final int FLOAT = 4; - - /** - * Double precision floating point number. - */ - static final int DOUBLE = 5; - - /** - * String type that has case insensitive comparison. - */ - static final int CHARS_NOCASE = 6; // nowosad@200 - - /** - * Boolean type. - */ - static final int BOOLEAN = 7; - - /** - * Date type. - */ - static final int DATE = 8; // rnovais@567_2: stored as an int. - - /** - * Datetime type. - */ - static final int DATETIME = 9; // rnovais@567_2: stored as two ints. - - /** - * Blob type. - */ - static final int BLOB = 10; - - // Aggregation functions supported. - /** - * No aggregation function. - */ - static final int FUNCTION_AGG_NONE = -1; - - /** - * COUNT aggregation function. It counts the number of elements. - */ - static final int FUNCTION_AGG_COUNT = 0; - - /** - * MAX aggregation function. It gets the maximum value. - */ - static final int FUNCTION_AGG_MAX = 1; - - /** - * MIN aggregation function. It gets the minimum value. - */ - static final int FUNCTION_AGG_MIN = 2; - - /** - * AVG aggregation function. It calculates the average of the values. - */ - static final int FUNCTION_AGG_AVG = 3; - - /** - * SUM aggregation function. It sumns the values. - */ - static final int FUNCTION_AGG_SUM = 4; - - // rnovais@568_10: supported data type functions. - /** - * No function. - */ - static final int FUNCTION_DT_NONE = -1; - - /** - * YEAR function. It returns the year of a date or a datetime data type. - */ - static final int FUNCTION_DT_YEAR = 0; - - /** - * MONTH function. It returns the month of a date or a datetime data type. - */ - static final int FUNCTION_DT_MONTH = 1; - - /** - * DAY function. It returns the day of a date or a datetime data type. - */ - static final int FUNCTION_DT_DAY = 2; - - /** - * HOUR function. It returns the hours of a datetime data type. - */ - static final int FUNCTION_DT_HOUR = 3; - - /** - * MINUTE function. It returns the minutes of a datetime data type. - */ - static final int FUNCTION_DT_MINUTE = 4; - - /** - * SECOND function. It returns the seconds of a datetime data type. - */ - static final int FUNCTION_DT_SECOND = 5; - - /** - * MILLIS function. It returns the milli secconds of a datetime data type. - */ - static final int FUNCTION_DT_MILLIS = 6; - - /** - * ABS function. It returns the absolute value of a number data type. - */ - static final int FUNCTION_DT_ABS = 7; // rnovais@570_1 - - /** - * UPPER function. It converts the characters of a string to upper case. - */ - static final int FUNCTION_DT_UPPER = 8; // rnovais@570_1 - - /** - * LOWER function. It converts the characters of a string to lower case. - */ - static final int FUNCTION_DT_LOWER = 9; // rnovais@570_1 - - // juliana@250_8: now the maximum number of columns, fields, tables, etc is 254 instead of 128 except on palm. - // Maximum constants. - /** - * Maximum number of columns supported in a column list clause. - */ - static int MAX_NUM_COLUMNS = 254; // guich@561_1 - - /** - * Maximum number of parameters supported. - */ - static int MAX_NUM_PARAMS = 254; // guich@561_1 - - // Available operand / operator types. - /** - * No operand / operator. - */ - static final int OP_NONE = 0; - - /** - * Boolean operator AND. - */ - static final int OP_BOOLEAN_AND = 1; - - /** - * Boolean operator OR. - */ - static final int OP_BOOLEAN_OR = 2; - - /** - * Boolean operator NOT. - */ - static final int OP_BOOLEAN_NOT = 3; - - // juliana@253_9: improved Litebase parser. - /** - * Relational operator <. - */ - static final int OP_REL_LESS = LitebaseParser.TK_LESS; - - /** - * Relational operator =. - */ - static final int OP_REL_EQUAL = LitebaseParser.TK_EQUAL; - - /** - * Relational operator >. - */ - static final int OP_REL_GREATER = LitebaseParser.TK_GREATER; - - /** - * Relational operator >=. - */ - static final int OP_REL_GREATER_EQUAL = LitebaseParser.TK_GREATER_EQUAL; - - /** - * Relational operator <=. - */ - static final int OP_REL_LESS_EQUAL = LitebaseParser.TK_LESS_EQUAL; - - /** - * Relational operator != or <>. - */ - static final int OP_REL_DIFF = LitebaseParser.TK_DIFF; - - /** - * The operand is an identifier. - */ - static final int OP_IDENTIFIER = 10; - - /** - * String operator LIKE. - */ - static final int OP_PAT_MATCH_LIKE = 11; - - /** - * String operator NOT LIKE. - */ - static final int OP_PAT_MATCH_NOT_LIKE = 12; - - /** - * Operator IS. - */ - static final int OP_PAT_IS = 13; - - /** - * Operator IS NOT. - */ - static final int OP_PAT_IS_NOT = 14; - - /** - * Operand NULL. - */ - static final int OP_PAT_NULL = 15; - - /** - * hashCode("rowid"); - */ - static final int hcRowId = 108705909; - - /** - * The maximum length of the name of a table when is created as a plain file. - */ - static final int MAX_TABLE_NAME_LENGTH_AS_PLAIN_FILE = 23; - - // Aggregation functions table. - /** - * Array containing aggregation function names. - */ - static final String[] aggregateFunctionsNames = {"count", "max", "min", "avg", "sum" }; - - // juliana@226_5 - /** - * Array containing aggregation function types. - */ - static final byte[] aggregateFunctionsTypes = {INT, UNDEFINED, UNDEFINED, DOUBLE, DOUBLE}; - - // rnovais@_568_10 @_570_1 - // Data type functions table. - /** - * Array containing data type function names. - */ - static final String[] dataTypeFunctionsNames = {"year", "month", "day", "hour", "minute", "second", "millis", "abs", "upper", "lower"}; - - /** - * Array containing data type function types. - */ - static final byte[] dataTypeFunctionsTypes = {SHORT, SHORT, SHORT, SHORT, SHORT, SHORT, SHORT, UNDEFINED, CHARS, CHARS}; - - // rnovais@_568_10 - /** - * Functions X data type. Each row corresponds to one data type: CHARS, SHORT, - * INT, LONG, FLOAT, DOUBLE, CHARS_NOCASE, - * BOOLEAN, DATE, and DATETIME. The order can't be changed and it contains the - * function codes supported by the data types. - */ - static final byte[][] function_x_datatype = { - {FUNCTION_DT_UPPER, FUNCTION_DT_LOWER}, // rnovais@_570_1 - {FUNCTION_DT_ABS}, // rnovais@_570_5 - {FUNCTION_DT_ABS}, // rnovais@_570_1 - {FUNCTION_DT_ABS}, // rnovais@_570_5 - {FUNCTION_DT_ABS}, // rnovais@_570_5 - {FUNCTION_DT_ABS}, // rnovais@_570_5 - {FUNCTION_DT_UPPER, FUNCTION_DT_LOWER}, // rnovais@_570_1 - {}, - {FUNCTION_DT_YEAR, FUNCTION_DT_MONTH, FUNCTION_DT_DAY}, - {FUNCTION_DT_YEAR, FUNCTION_DT_MONTH, FUNCTION_DT_DAY, FUNCTION_DT_HOUR, FUNCTION_DT_MINUTE, FUNCTION_DT_SECOND, FUNCTION_DT_MILLIS}}; -} diff --git a/LitebaseSDK/src/java/litebase/SQLFieldDefinition.java b/LitebaseSDK/src/java/litebase/SQLFieldDefinition.java deleted file mode 100644 index 9b34acb146..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLFieldDefinition.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Represents a field of a statement except for SELECTs. - */ -@Deprecated -class SQLFieldDefinition -{ - /** - * The name of the field. - */ - String fieldName; - - /** - * The type of the field. It can be: NUMBER, UNDEFINED, CHARS, - * SHORT, INT, LONG, FLOAT, DOUBLE, - * CHARS_NOCASE, BOOLEAN, DATE, or DATE_TIME. - */ - int fieldType; - - /** - * Only used for chars / chars no case / blob types. For other types it is equal to zero. - */ - int fieldSize; - - /** - * Indicates if the field is the primary key. - */ - boolean isPrimaryKey; - - /** - * The default value of a field can contain a string, a number or a Date/Datetime value. This must be converted to the correct type later. If - * defaultValue is null, the default value was not defined. - */ - String defaultValue; - - /** - * Defines if the field can be null or not. - */ - boolean isNotNull; - - /** - * Constructs a new SQLFieldDefinition object. - * - * @param aFieldName The name of the new field. - * @param aFieldType The type of the new field. - * @param aFieldSize The size of the new field (it is zero if the type is not CHARS or CHARS_NOCASE). - * @param anIsPrimaryKey Indicates if the new field is the primary key. - * @param aDefaultValue The default value of the new field (it can be null if there is no default value). - * @param anIsNotNull Indicates if the new field can be null or not. - */ - public SQLFieldDefinition(String aFieldName, int aFieldType, int aFieldSize, boolean anIsPrimaryKey, String aDefaultValue, boolean anIsNotNull) - { - fieldName = aFieldName; - fieldType = aFieldType; - fieldSize = aFieldSize; - isPrimaryKey = anIsPrimaryKey; - defaultValue = aDefaultValue; - isNotNull = anIsNotNull; - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLInsertStatement.java b/LitebaseSDK/src/java/litebase/SQLInsertStatement.java deleted file mode 100644 index 027c2bf8cb..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLInsertStatement.java +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.InvalidDateException; - -/** - * Internal use only. Represents a SQL INSERT statement. - */ -@Deprecated -class SQLInsertStatement extends SQLStatement -{ - /** - * The base table used by the SQL expression. - */ - Table table; - - /** - * The table name. - */ - String tableName; - - /** - * The fields used if the insert statement is not using the default order. - */ - String[] fields; - - /** - * The record to be inserted. - */ - SQLValue[] record; - - /** - * The number of the parameters if the insert statement is a preprared statement. - */ - int paramCount; - - // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - /** - * The array with the indexes of the parameters. - */ - short[] paramIndexes; - - /** - * An array that indicates if a parameters is defined or not. - */ - boolean[] paramDefined; - - /** - * An array that indicates if a null value will be stored in a field. - */ - byte[] storeNulls; - - /** - * Constructs an insert statement given the result of the parsing process. - * - * @param parser The result of the parsing process. - * @param driver The connection with Litebase - * @throws SQLParseException If there is a field named "rowid". - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - */ - SQLInsertStatement(LitebaseParser parser, LitebaseConnection driver) throws SQLParseException, InvalidDateException, IOException - { - int nFields = parser.fieldNamesSize + 1; // Number of fields + rowid. - String value; - - type = SQLElement.CMD_INSERT; - - // On Litebase, a table has no alias name on insert. This has no sense. So the same name of the table will be used as an alias. The parser must - // be changed to understand the alias table name. - - table = driver.getTable(tableName = parser.tableList[0].tableName); // Gets the statement base table. - - if (parser.fieldNamesSize != 0) // Checks if it is not using the default order. - { - // Gets the fields and stores them. - String[] fieldsAux = fields = new String[nFields]; - String[] fieldNames = parser.fieldNames; - storeNulls = new byte[(nFields + 7) >> 3]; - while (--nFields > 0) - // A field cannot have the same hash code of the rowid. - if ((fieldsAux[nFields] = fieldNames[nFields - 1]).hashCode() == SQLElement.hcRowId) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_ROWID_CANNOT_BE_CHANGED)); - } - else // The nulls info does not need to be recreated when all the fields are used in the insert. - Convert.fill(storeNulls = table.storeNulls, 0, storeNulls.length, 0); - - // Number of fields + rowid. - SQLValue[] recordAux = record = SQLValue.newSQLValues(nFields = parser.fieldValuesSize + 1); // Gets the values and stores them. - byte[] storeNullsAux = storeNulls; - String[] fieldValues = parser.fieldValues; - - // Allocates space for the list of the parameters. Worst case: all fields are parameters. - // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - paramIndexes = new short[nFields]; - paramDefined = new boolean[nFields]; - - while (--nFields > 0) - if ((value = fieldValues[nFields - 1]) != null) // Only stores values that are not null. - recordAux[nFields].asString = value; - else - Utils.setBit(storeNullsAux, nFields, recordAux[nFields].isNull = true); - } - - /** - * Sets the value of a short parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the short parameter. - */ - void setParamValue(int index, short val) - { - int columnIndex = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - checkIndex(index, columnIndex, SQLElement.SHORT, SQLElement.UNDEFINED); // Checks the parameter index and type. - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asShort = val; - - Utils.setBit(storeNulls, columnIndex, value.isNull = false); // The value is not null. - } - - /** - * Sets the value of an integer parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the integer parameter. - */ - void setParamValue(int index, int val) - { - int columnIndex = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - checkIndex(index, columnIndex, SQLElement.INT, SQLElement.UNDEFINED); // Checks the parameter index and type. - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asInt = val; - - Utils.setBit(storeNulls, columnIndex, value.isNull = false); // The value is not null. - } - - /** - * Sets the value of a long parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the long parameter. - */ - void setParamValue(int index, long val) - { - int columnIndex = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - checkIndex(index, columnIndex, SQLElement.LONG, SQLElement.UNDEFINED); // Checks the parameter index and type. - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asLong = val; - - Utils.setBit(storeNulls, columnIndex, value.isNull = false); // The value is not null. - } - - /** - * Sets the value of a float parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the float parameter. - */ - void setParamValue(int index, float val) - { - int columnIndex = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - checkIndex(index, columnIndex, SQLElement.FLOAT, SQLElement.UNDEFINED); // Checks the parameter index and type. - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asDouble = val; - - Utils.setBit(storeNulls, columnIndex, value.isNull = false); // The value is not null. - } - - /** - * Sets the value of a double parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the double parameter. - */ - void setParamValue(int index, double val) throws DriverException - { - int columnIndex = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - checkIndex(index, columnIndex, SQLElement.DOUBLE, SQLElement.UNDEFINED); // Checks the parameter index and type. - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asDouble = val; - - Utils.setBit(storeNulls, columnIndex, value.isNull = false); // The value is not null. - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Sets the value of a string parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the string parameter. - * @throws DriverException If the parameter column type is BLOB. - * @throws IllegalArgumentException If the index is out of range. - */ - void setParamValue(int index, String val) throws DriverException, IllegalArgumentException - { - if (index < 0 || index >= paramCount) // Checks if the index is within the range of the parameter count. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - int columnIndex = paramIndexes[index] & 0xFF; - - if (table.columnTypes[columnIndex] == SQLElement.BLOB) // The type can't be a blob. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_STRING)); - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asString = val; - Utils.setBit(storeNulls, columnIndex, value.isNull = (val == null)); // Sets whether the value is or not null. - - paramDefined[index] = true; - } - - /** - * Sets the value of a array of bytes (blob) parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the string parameter. - */ - void setParamValue(int index, byte[] val) - { - int columnIndex = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - checkIndex(index, columnIndex, SQLElement.BLOB, SQLElement.UNDEFINED); // Checks the parameter index and type. - - // Sets the values of the parameter in its list. - SQLValue value = record[columnIndex]; - value.asBlob = val; - Utils.setBit(storeNulls, columnIndex, value.isNull = (val == null)); // Sets whether the value is or not null. - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - // juliana@223_3: PreparedStatement.setNull() now works for blobs. - /** - * Sets null in a given field. - * - * @param index The index of the parameter. - * @throws IllegalArgumentException If the index is out of range. - */ - void setNull(int index) throws DriverException - { - if (index < 0 || index >= paramCount) // Checks if the index is within the range of the parameter count. - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - int columnIndex = paramIndexes[index] & 0xFF; - SQLValue value = record[columnIndex]; - - // Sets the null value. - value.asBlob = null; - value.asString = null; - Utils.setBit(storeNulls, columnIndex, value.isNull = paramDefined[index] = true); // Sets whether the value is or not null. - } - - /** - * Clears all parameter values of a prepared statement insert. - */ - void clearParamValues() - { - int i = paramCount, - j; - SQLValue value; - SQLValue[] recordAux = record; - - // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - short[] paramIndexesAux = paramIndexes; - byte[] storeNullsAux = storeNulls; - - Convert.fill(paramDefined, 0, paramDefined.length, false); - - while (--i >= 0) - { - (value = recordAux[j = paramIndexesAux[i] & 0xFF]).asString = null; - Utils.setBit(storeNullsAux, j, value.isNull = false); - value.asBlob = null; - } - } - - /** - * Does nothing. If a parameter is not defined, null will be inserted instead. - */ - void allParamValuesDefined() {} - - /** - * Executes an insert statement. - * - * @param driver A connection with Litebase. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void litebaseDoInsert(LitebaseConnection driver) throws IOException, InvalidDateException - { - Table tableAux = table; - PlainDB plainDB = tableAux.db; - NormalFile dbFile = (NormalFile)plainDB.db; - - if (dbFile == null) // juliana@201_28: If a table is re-created after the prepared statement is parsed, there won't be a NPE. - table = tableAux = driver.getTable(tableName); - - tableAux.verifyNullValues(record, storeNulls,SQLElement.CMD_INSERT); - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since - // its last opening. - tableAux.setModified(); // Sets the table as not closed properly. - - tableAux.writeRecord(record, -1); - } - - /** - * Binds an insert statement. - * - * @return A binded insert statement. - * @throws SQLParseException If the number of values does not match the number of table fields. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - SQLInsertStatement litebaseBindInsertStatement() throws SQLParseException, IOException, InvalidDateException, InvalidNumberException - { - int valuesCount = record.length, - i = 0; - Table baseTable = table; // Gets the statement base table. - - paramCount = 0; - - while (++i < valuesCount) // Checks if there are undefined values. - { - String string; - if ((string = record[i].asString) != null && string.equals("?")) // Identifies the values that are placeholders for parameters. - paramIndexes[paramCount++] = (short)i; - } - - if (fields != null) // No fields: The values are ordered. - baseTable.reorder(this); - - if (record.length != baseTable.columnCount) // The record to be inserted size must math the table record size. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION)); - - baseTable.convertStringsToValues(record); // Converts the string values to their right types. - return this; - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Checks the prepared statement parameter index. - * - * @param index The index of the prepared statement parameter. - * @param columnIndex The column index of the parameter. - * @param type1 The main column type of the method. - * @param type2 Used only for strings to test if the column is a char nocase. - * @throws IllegalArgumentException If the index is out of range. - * @throws DriverException If the column is not of type requested. - */ - void checkIndex(int index, int columnIndex, int type1, int type2) throws IllegalArgumentException, DriverException - { - if (index < 0 || index >= paramCount) // Checks if the index is within the range. - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - int type = table.columnTypes[columnIndex]; - if (type != type1 && type != type2) // Check the column type. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - record[columnIndex].asString = null; - - paramDefined[index] = true; // The parameter will be defined. - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/litebase/SQLParseException.java b/LitebaseSDK/src/java/litebase/SQLParseException.java deleted file mode 100644 index 7f485665a5..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLParseException.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be dispatched by routines that handle the parsing of an SQL command. It is an unchecked Exception (it can be thrown any time). - */ -@Deprecated -public class SQLParseException extends RuntimeException -{ - /** - * The exception that caused this exception to be dispatched, or null if the cause of this exception was not another exception. - */ - public Exception cause; - - /** - * Constructs a new SQLParseException exception with the specified detail message. - * - * @param message the detail message. - */ - SQLParseException(String message) - { - super(message); - } - - /** - * Constructs a new SQLParseException exception with a detail message taken from the given exception. The cause can be accessed in - * the public member cause. Use it to get a better stack trace to the problem. - * - * @param causeException the exception that caused this one. - */ - SQLParseException(Exception causeException) - { - this(causeException.getMessage()); - cause = causeException; - } - - /** - * Prints this stack trace and also the trace of the cause, if any was set. - */ - public void printStackTrace() - { - if (cause != null) - cause.printStackTrace(); - super.printStackTrace(); - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLParseException4D.java b/LitebaseSDK/src/java/litebase/SQLParseException4D.java deleted file mode 100644 index ec217102b4..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLParseException4D.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_1: removed unused exception constructors and changed constructors visibility to package because it is not to be used by the user. -/** - * This exception may be dispatched by routines that handle the parsing of an SQL command. It is an unchecked Exception (it can be thrown any time). - */ -@Deprecated -public class SQLParseException4D extends RuntimeException -{ - // juliana@268_1: added field cause to SQLParseException for Windows 32, Windows CE, Palm, Android, and iOS. - /** - * The exception that caused this exception to be dispatched, or null if the cause of this exception was not another exception. - */ - public Exception cause; -} diff --git a/LitebaseSDK/src/java/litebase/SQLResultSetField.java b/LitebaseSDK/src/java/litebase/SQLResultSetField.java deleted file mode 100644 index f86f0e97d9..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLResultSetField.java +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Internal use only. Represents a field of a ResultSet. - */ -@Deprecated -class SQLResultSetField -{ - /** - * The column name hash code. - */ - int tableColHashCode; - - /** - * The field alias hash code. - */ - int aliasHashCode; - - /** - * The index of the column that this field represents in the underlying table. For virtual fields, this value equals -1. - */ - int tableColIndex = -1; - - /** - * Indicates what resultset it belongs. - */ - int indexRs; - - /** - * The sql function that this field represents. - */ - int sqlFunction = SQLElement.FUNCTION_AGG_NONE; - - /** - * The data type. - */ - int dataType; - - /** - * The size of the field; only used in chars types and blob. - */ - int size; - - /** - * Indicates the index to use when doing a max() or min() operation. - */ - int index = -1; // juliana@230_21: MAX() and MIN() now use indices on simple queries. - - /** - * Indicates that the index to be used is composed or not. - */ - boolean isComposed; // juliana@230_21: MAX() and MIN() now use indices on simple queries. - - /** - * Indicates if this is a wildcard field. - */ - boolean isWildcard; - - /** - * Indicates if the field represents a virtual column (not mapped directly to the underlying table). - */ - boolean isVirtual; - - /** - * Indicates if the function is a data type function. - */ - boolean isDataTypeFunction; - - /** - * Indicates if the function is an aggregated function. - */ - boolean isAggregatedFunction; - - /** - * Indicates if the result is to be shown in ascending or decreasing order for fields from order by clause. - */ - boolean isAscending = true; - - /** - * The name of the column that this field represents in the underlying table. For virtual fields, this value equals null. - */ - String tableColName; - - /** - * The field alias. - */ - String alias; - - /** - * Indicates the table name it belongs. The parser sets its value. e. g.
    - * select person.age from test: tableName = person. - */ - String tableName; - - /** - * Indicates what table it belongs. - */ - Table table; - - /** - * The parameter of the function.
    - * Note: It is declared as ResultSetField to allow nested function calls in the future. - */ - SQLResultSetField parameter; - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - /** - * Finds the best index to use in a min() or max() operation. - */ - void findMaxMinIndex() - { - Table tableAux = table; - int column = parameter != null? parameter.tableColIndex : tableColIndex, // juliana@230_29 - i = tableAux.numberComposedIndices; - ComposedIndex[] composedIndices = tableAux.composedIndices; - - if (tableAux.columnIndices[column] != null) // If the field has a simple index, uses it. - { - index = column; - isComposed = false; - } - else - while (--i >= 0) - if (composedIndices[i].columns[0] == column) // Else, if the field is the first field of a composed index, uses it. - { - index = i; - isComposed = true; - break; - } - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/litebase/SQLResultSetTable.java b/LitebaseSDK/src/java/litebase/SQLResultSetTable.java deleted file mode 100644 index d5aeae47de..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLResultSetTable.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -/** - * Internal use only. Represents a table of various statements, except for inserts. - */ -@Deprecated -class SQLResultSetTable -{ - /** - * The object table, filled by bindSelectStatement(). - */ - Table table; - - /** - * The name of the table, filled during the parsing process. - */ - String tableName; - - /** - * The Table alias. - */ - String aliasTableName; - - /** - * The alias table name hash code. - */ - int aliasTableNameHashCode; - - /** - * Constructs a new SQLResultSetTable object using a table name and its optional alias name. - * - * @param aTableName the name of the new table. - * @param anAliasTableName the optional alias name of the new table (it can be null). - */ - SQLResultSetTable(String aTableName, String anAliasTableName) - { - tableName = aTableName; // Sets the table name. - - // If the alias is null, it receives the name of the table. If the alias is not null, the alias name is set with the alias passed as a - // parameter. - aliasTableNameHashCode = (aliasTableName = (anAliasTableName == null) ? aTableName : anAliasTableName).hashCode(); - } - - /** - * Constructs a new SQLResultSetTable object using just a table name. - * @param aTableName - */ - public SQLResultSetTable(String aTableName) - { - tableName = aTableName; - } - -} diff --git a/LitebaseSDK/src/java/litebase/SQLSelectClause.java b/LitebaseSDK/src/java/litebase/SQLSelectClause.java deleted file mode 100644 index 2e1a5f1ef2..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLSelectClause.java +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.Convert; -import totalcross.util.IntHashtable; - -/** - * Internal use only. Represents the "select" clause portion of a SQL query. - */ -@Deprecated -class SQLSelectClause -{ - // Possibles types for the select clause - /** - * Indicates if the query is of the form select count(*) from table where.... - */ - static final int COUNT_WITH_WHERE = 1; - - // juliana@250_8: now the maximum number of columns, fields, tables, etc is 254 instead of 128 except on palm. - /** - * Maximum number of fields supported - */ - static final int MAX_NUM_FIELDS = 254; - - /** - * The resulting ResultSet field list. - */ - SQLResultSetField[] fieldList = new SQLResultSetField[MAX_NUM_FIELDS]; - - /** - * Number of fields found. - */ - int fieldsCount; - - /** - * Indicates if the select clause has aggregated functions. - */ - boolean hasAggFunctions; - - /** - * Indicates if the select clause has real columns. - */ - boolean hasRealColumns; - - /** - * The resulting ResultSet table list. - */ - SQLResultSetTable[] tableList = new SQLResultSetTable[MAX_NUM_FIELDS]; - - /** - * The index of the fields. - */ - IntHashtable htName2index; - - // This will be better used when implement a PLANNER for litebase is implemented. - // Up to now this only indicates if the select clause has a count(*). - /** - * Indicates the type of the select clause. - */ - int type = -1; - - /** - * Binds the column information of the underlying tables to the select clause. - * - * @param driver The Litebase connection. - * @throws SQLParseException In case of an unknown or ambiguous column name, the parameter and the function data types are incompatible, or the - * total number of fields of the select exceeds the maximum. - */ - void bindColumnsSQLSelectClause(LitebaseConnection driver) throws SQLParseException - { - int i, - j, - n; - Table table; - SQLResultSetTable rsTable; - SQLResultSetField field; - StringBuffer sbufnf = driver.sBuffer; - - // If the select clause has a wild card (is null), then expands the list using the column information from the given tables. - if (fieldList == null) - { - String tableName; - String[] columnNames; - int[] columnHashes; - byte[] columnTypes; - int[] columnSizes; - int pos = 0, - count = 0, - m; - - j = n = tableList.length; - while (--j >= 0) - count += tableList[j].table.columnCount - 1; // Excludes the rowid. - - // juliana@250_7: now a select * will cause a SQLParseException if the total number of columns is more than 254. - if (count > SQLElement.MAX_NUM_COLUMNS) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_FIELDS_OVERFLOW)); - - fieldsCount = count; - fieldList = new SQLResultSetField[fieldsCount]; - - j = -1; - count = 0; - while (++j < n) - { - tableName = (rsTable = tableList[j]).tableName; - columnNames = (table = rsTable.table).columnNames; - columnHashes = table.columnHashes; - columnTypes = table.columnTypes; - columnSizes = table.columnSizes; - m = table.columnCount; - i = 0; - while (++i < m) // guich@503_10: excludes the rowid. - { - (field = new SQLResultSetField()).alias = columnNames[i]; // guich@_512_2: added column name so the ResultSetMetaData can work. - field.aliasHashCode = field.tableColHashCode = columnHashes[i]; - field.dataType = columnTypes[i]; - field.size = columnSizes[i]; - field.tableColIndex = i; - sbufnf.setLength(0); - htName2index.put(Convert.hashCode(sbufnf.append(tableName).append('.').append(field.alias)), pos); - - if (!htName2index.exists(field.aliasHashCode)) - htName2index.put(field.aliasHashCode, pos); - - field.table = table; - field.tableName = rsTable.tableName; - fieldList[count++] = field; - pos++; - } - } - } - else - { - int index, - hash, - auxIndex, - aggFunctionType, - dtFunctionType, // initialized with a UNDEFINED number - sqlFunction; - boolean foundFirst; - String tableName; - Table auxTable; - SQLResultSetTable rsTableAux; - SQLResultSetField param; - - n = fieldsCount; - i = -1; - - while (++i < n) // Binds the listed colums to the table. - { - field = fieldList[i]; - rsTable = null; - index = -1; - table = null; - - // Aggregation functions (count() doesn't have a column name yet). - if (!field.isAggregatedFunction || field.sqlFunction != SQLElement.FUNCTION_AGG_COUNT) - { - if ((tableName = field.tableName) != null) // Checks the names. - { - j = tableList.length; - while (--j >= 0) // Verifies if it is a valid table name. - if (tableList[j].aliasTableName.equals(tableName)) - { - table = (rsTable = tableList[j]).table; - break; - } - if (table == null) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - if ((index = table.htName2index.get(hash = field.tableColName.hashCode(), -1)) == -1) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - htName2index.put(field.aliasHashCode, i); - sbufnf.setLength(0); - - // Used an explicit alias. - if (field.aliasHashCode != Convert.hashCode(sbufnf.append(tableName).append('.').append(field.tableColName))) - { - sbufnf.setLength(0); - htName2index.put(Convert.hashCode(sbufnf.append(tableName).append('.').append(field.alias)), i); - } - else - if (!htName2index.exists(hash)) - htName2index.put(hash, i); // Stores the name of the field once; only the first. - } - else // Verifies if the column name in the field list is ambiguous. - { - table = null; - rsTable = null; - foundFirst = false; - j = tableList.length; - while (--j >= 0) - { - auxIndex = (auxTable = (rsTableAux = tableList[j]).table).htName2index.get(field.tableColHashCode, -1); - if (auxIndex != -1) - { - if (foundFirst) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_AMBIGUOUS_COLUMN_NAME) + field.alias); - else - { - foundFirst = true; - index = auxIndex; - sbufnf.setLength(0); - htName2index.put(Convert.hashCode(sbufnf.append(tableList[j].tableName).append('.').append(field.alias)), i); - htName2index.put(field.aliasHashCode, i); - - // juliana@252_4: corrected the fact that a field used in a function can't be fetched using only the name of the field - // unless it is also in the select field list. - if (field.sqlFunction == SQLElement.FUNCTION_DT_NONE) - htName2index.put(field.tableColHashCode, i); - - table = auxTable; - rsTable = rsTableAux; - } - } - } - if (!foundFirst) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_UNKNOWN_COLUMN) + field.alias); - } - } - - if (!field.isVirtual) // If the field is not virtual, it needs to be mapped directly to the underlying table. - { - field.dataType = table.columnTypes[field.tableColIndex = index]; - field.size = table.columnSizes[index]; - field.table = table; - field.tableName = rsTable.tableName; - htName2index.put(field.aliasHashCode, i); - } - else - if (field.isAggregatedFunction || field.isDataTypeFunction) // If it is an aggregated or data type function, maps its parameter. - { - param = field.parameter; - aggFunctionType = dtFunctionType = SQLElement.UNDEFINED; // initialized with an UNDEFINED number. - sqlFunction = field.sqlFunction; - field.table = table; // rnovais@200_4 - htName2index.put(field.aliasHashCode, i); - - if (field.isAggregatedFunction) // Stores the correct function code. - aggFunctionType = SQLElement.aggregateFunctionsTypes[sqlFunction]; - else - dtFunctionType = SQLElement.dataTypeFunctionsTypes[sqlFunction]; - - if (param != null) - { - param.dataType = table.columnTypes[param.tableColIndex = index]; - param.size = table.columnSizes[index]; - - if (field.isAggregatedFunction) // rnovais@568_10 - { - // Checks if the parameter and aggregated function data types are compatible. - // juliana@226_5 - if ((aggFunctionType == SQLElement.INT || aggFunctionType == SQLElement.DOUBLE) - && (param.dataType == SQLElement.CHARS || param.dataType == SQLElement.CHARS_NOCASE)) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES) + ' ' - + SQLElement.aggregateFunctionsNames[sqlFunction]); - - // For aggregated functions, if the function does not have a defined data type, it inherits the parameter size and type. - if (aggFunctionType == SQLElement.UNDEFINED) - { - field.dataType = param.dataType; - field.size = param.size; - } - } - else - { - field.size = param.size; // rnovais@570_1 - - // rnovais@570_5: if UNDEFINED the datatype will be the same of the field thus, it is possible to have functions that can be - // applyed to diferents fieds type. e.g. ABS(int) returns int, ABS(double) returns double, etc. - if (dtFunctionType == SQLElement.UNDEFINED) - field.dataType = param.dataType; - - // Checks if the parameter and the data type function data types are compatible. - Utils.bindFunctionDataType(param.dataType, sqlFunction); - } - } - } - } - } - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLSelectStatement.java b/LitebaseSDK/src/java/litebase/SQLSelectStatement.java deleted file mode 100644 index a47431b0c8..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLSelectStatement.java +++ /dev/null @@ -1,1727 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Internal use only. Represents a SQL SELECT statement. - */ -@Deprecated -class SQLSelectStatement extends SQLStatement -{ - /** - * The select clause of the statement. - */ - private SQLSelectClause selectClause; - - /** - * The where clause of the statement. - */ - SQLBooleanClause whereClause; - - /** - * The group by clause of the statement. - */ - SQLColumnListClause groupByClause; // juliana@226_14 - - /** - * The having clause of the statement. - */ - private SQLBooleanClause havingClause; - - /** - * The order by clause of the statement. - */ - SQLColumnListClause orderByClause; - - /** - * Creates a new select statement for a SQL SELECT query. - * - * @param parser The result of the parsing process. - */ - SQLSelectStatement(LitebaseParser parser) - { - type = SQLElement.CMD_SELECT; // Sets the type of statement. - - // Sets the select clause and its hash table. - (selectClause = parser.select).htName2index = new IntHashtable(parser.select.fieldsCount == 0? 3 : parser.select.fieldsCount); - - whereClause = parser.whereClause; // Sets the where clause. - groupByClause = parser.groupBy; // Sets the group by clause. - havingClause = parser.havingClause; // Sets the having clause. - orderByClause = parser.orderBy; // Sets the order by clause. - } - - /** - * Sets the value of a short parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the short parameter. - * @throws DriverException If the parameter index is invalid. - */ - void setParamValue(int index, short val) throws DriverException - { - SQLBooleanClause whereClauseAux = whereClause, - havingClauseAux = havingClause; - int whereParamCount = (whereClauseAux == null? 0 : whereClauseAux.paramCount); // Gets the where clause parameter count. - - if (index < 0 || index >= (whereParamCount + (havingClauseAux == null? 0 : havingClauseAux.paramCount))) // Checks if the index is within the range. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - if (index < whereParamCount) // Sets the parameter value in its proper place. - whereClauseAux.paramList[index].setParamValue(val); - else - havingClauseAux.paramList[index - whereParamCount].setParamValue(val); - } - - /** - * Sets the value of a integer parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the integer parameter. - * @throws DriverException If the parameter index is invalid. - */ - void setParamValue(int index, int val) throws DriverException - { - SQLBooleanClause whereClauseAux = whereClause, - havingClauseAux = havingClause; - int whereParamCount = (whereClauseAux == null? 0 : whereClauseAux.paramCount); // Gets the where clause parameter count. - - if (index < 0 || index >= (whereParamCount + (havingClauseAux == null? 0 : havingClauseAux.paramCount))) // Checks if the index is within the range. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - if (index < whereParamCount) // Sets the parameter value in its proper place. - whereClauseAux.paramList[index].setParamValue(val); - else - havingClauseAux.paramList[index - whereParamCount].setParamValue(val); - } - - /** - * Sets the value of a long parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the long parameter. - * @throws DriverException If the parameter index is invalid. - */ - void setParamValue(int index, long val) throws DriverException - { - SQLBooleanClause whereClauseAux = whereClause, - havingClauseAux = havingClause; - int whereParamCount = (whereClauseAux == null? 0 : whereClauseAux.paramCount); // Gets the where clause parameter count. - - if (index < 0 || index >= (whereParamCount + (havingClauseAux == null? 0 : havingClauseAux.paramCount))) // Checks if the index is within the range. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - if (index < whereParamCount) // Sets the parameter value in its proper place. - whereClauseAux.paramList[index].setParamValue(val); - else - havingClauseAux.paramList[index - whereParamCount].setParamValue(val); - } - - /** - * Sets the value of a float parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the float parameter. - * @throws DriverException If the parameter index is invalid. - */ - void setParamValue(int index, float val) throws DriverException - { - SQLBooleanClause whereClauseAux = whereClause, - havingClauseAux = havingClause; - int whereParamCount = (whereClauseAux == null ? 0 : whereClauseAux.paramCount); // Gets the where clause parameter count. - - if (index < 0 || index >= (whereParamCount + (havingClauseAux == null? 0 : havingClauseAux.paramCount))) // Checks if the index is within the range. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - if (index < whereParamCount) // Sets the parameter value in its proper place. - whereClauseAux.paramList[index].setParamValue(val); - else - havingClauseAux.paramList[index - whereParamCount].setParamValue(val); - } - - /** - * Sets the value of a double parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the double parameter. - * @throws DriverException If the parameter index is invalid. - */ - void setParamValue(int index, double val) throws DriverException - { - SQLBooleanClause whereClauseAux = whereClause, - havingClauseAux = havingClause; - int whereParamCount = (whereClauseAux == null ? 0 : whereClauseAux.paramCount); // Gets the where clause parameter count. - - if (index < 0 || index >= (whereParamCount + (havingClauseAux == null ? 0 : havingClauseAux.paramCount))) // Checks if the index is within the range. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - if (index < whereParamCount) // Sets the parameter value in its proper place. - whereClauseAux.paramList[index].setParamValue(val); - else - havingClauseAux.paramList[index - whereParamCount].setParamValue(val); - } - - /** - * Sets the value of a string parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the string parameter. - * @throws DriverException If the parameter index is invalid. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @throws InvalidNumberException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void setParamValue(int index, String val) throws DriverException, SQLParseException, InvalidNumberException, InvalidDateException - { - SQLBooleanClause whereClauseAux = whereClause, - havingClauseAux = havingClause; - int whereParamCount = (whereClauseAux == null ? 0 : whereClauseAux.paramCount); // Gets the where clause parameter count. - - if (index < 0 || index >= (whereParamCount + (havingClauseAux == null? 0 : havingClauseAux.paramCount))) // Checks if the index is within the range. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - - if (val == null) // A null can't be in a parameter of a where clause. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAM_NULL)); - - if (index < whereParamCount) // Sets the parameter value in its proper place. - whereClauseAux.paramList[index].setParamValue(val); - else - havingClauseAux.paramList[index - whereParamCount].setParamValue(val); - } - - // juliana@223_3: PreparedStatement.setNull() now works for blobs. - /** - * This was expected to set null in a given field. Since a null can't be in a parameter of a where clause, this method will only raise an - * exception. - * - * @param index The index of the parameter. Not used since a null can't be in a parameter of a where clause. - * @throws SQLParserException A null can't be in a parameter of a where clause. - */ - void setNull(int index) throws SQLParseException - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAM_NULL)); - } - - /** - * This was expected to set the value of a byte array parameter at the given index. Since blobs can't be in a where clause and a select clause - * only has parameters in a where clause, this method will only raise an exception. - * - * @param index The index of the parameter. - * @param val The value of the blob parameter. - * @throws SQLParseException Blobs can't be in a where clause. - */ - void setParamValue(int index, byte[] val) - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_WHERE)); - } - - /** - * Clears all parameter values of a prepared statement select. - */ - void clearParamValues() - { - SQLBooleanClauseTree[] paramList; - SQLBooleanClause clause; - int i; - - if ((clause = whereClause) != null) // Clears all the parameters of the where clause. - { - paramList = clause.paramList; - i = clause.paramCount; - while (--i >= 0) - paramList[i].isParamValueDefined = false; - } - - if ((clause = havingClause) != null) // Clears all the parameters of the having clause. - { - paramList = clause.paramList; - i = clause.paramCount - 1; - while (--i >= 0) - paramList[i].isParamValueDefined = false; - } - } - - /** - * Checks if all the parameters values are defined. - * - * @throws DriverException If not all parameter values are defined. - */ - void allParamValuesDefined() throws DriverException - { - int i; - SQLBooleanClauseTree[] paramList; - SQLBooleanClause clause; - - if ((clause = whereClause) != null) // Checks if all the where clause parameters are defined. - { - paramList = clause.paramList; - i = clause.paramCount; - while (--i >= 0) - if (!paramList[i].isParamValueDefined) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_NOT_ALL_PARAMETERS_DEFINED)); - } - - if ((clause = havingClause) != null) // Checks if all the having clause parameters are defined. - { - paramList = clause.paramList; - i = clause.paramCount; - while (--i >= 0) - if (!paramList[i].isParamValueDefined) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_NOT_ALL_PARAMETERS_DEFINED)); - } - // All parameters are defined. - } - - // Modified to support the new SQL clauses (ORDER BY, GROUP BY, HAVING). - // Modified to use temporary tables except when the query is a simple select (select * from tablename). - /** - * Executes the select statement. - * - * @param driver The connection with Litebase. - * @return The query result set. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - ResultSet litebaseDoSelect(LitebaseConnection driver) throws IOException, InvalidDateException, InvalidNumberException - { - Table rsBaseTable; - boolean isSimpleSelect = false; - SQLResultSetTable[] tableList = selectClause.tableList; - int i = tableList.length; - - while (--i >= 0) // juliana@201_28: If a table is re-created after the prepared statement is parsed, there won't be a NPE. - if (tableList[i].table.db.db == null) - tableList[i].table = driver.getTable(tableList[i].tableName); - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@114_10: simple selects do not use temporary tables. - // juliana@212_4: if the select fields are in the table order beginning with rowid, do not build a temporary table. - if (groupByClause == null && havingClause == null && orderByClause == null && whereClause == null && !selectClause.hasAggFunctions - && selectClause.tableList.length == 1) - { - isSimpleSelect = true; - rsBaseTable = selectClause.tableList[0].table; - rsBaseTable.answerCount = -1; - } - else - { - rsBaseTable = generateResultSetTable(driver); // Temporary table. - - if (rsBaseTable.name == null) - { - // Remaps the table column names to use the aliases of the select statement instead of the original column names. - rsBaseTable.remapColumnsNames2Aliases(selectClause.fieldList); - - // This must be used only for temporary tables. - rsBaseTable.plainShrinkToSize(); // guich@201_9: always shrink the .db and .dbo memory files. - } - } - - // Creates the result set without passing any fields or WHERE clause, since all the records and all the fields in the temporary table are part - // of the result set. - ResultSet rs = rsBaseTable.createResultSetForSelect(null); - - rs.htName2index = selectClause.htName2index; // Stores the hash table. - rs.fields = selectClause.fieldList; // Stores the field list for the meta data. - rs.isSimpleSelect = isSimpleSelect; // juliana@114_10: indicates if it is a simple select or not. - rs.driver = driver; // juliana@220_3 - - if (rsBaseTable.answerCount >= 0) - { - rs.answerCount = rsBaseTable.answerCount; - - // juliana@263_3: corrected a bug where a new result set data could overlap an older result set data if both were related to the same table. - Vm.arrayCopy(rsBaseTable.allRowsBitmap, 0, rs.allRowsBitmap = new byte[rsBaseTable.allRowsBitmap.length], 0, - rsBaseTable.allRowsBitmap.length); - } - return rs; - } - - /** - * Binds a select statement. - * - * @param driver The Litebase connection. - * @return A select statement binded. - * @throws InvalidDateException If an internal method throws it. - * @throws IOException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - SQLSelectStatement litebaseBindSelectStatement(LitebaseConnection driver) throws InvalidDateException, IOException, InvalidNumberException - { - // Gets all tables from the select statement. - SQLResultSetTable[] list = selectClause.tableList; - int i = list.length; - while (--i >= 0) - list[i].table = driver.getTable(list[i].tableName); - - bindSelectStatement(driver); // Validates and binds the select statement to the table list. - orderTablesToJoin(); // Finds the best table order for the join operation. - validateSelectStatement(); // Validates the select statement. - return this; - } - - /** - * Tries to put as inner table a table that has an index used more often in the where clause, when the where clause has a comparison between - * fields from different tables. e.g.: select * from table1, table2 where table1.field1 = table2.field2 If only - * table1.field1 has index, changes the select to: select * from table2, table1 where table1.field1 = table2.field2. - * If both tables has the same level of index using, sorts them by the row count. - */ - private void orderTablesToJoin() - { - // Gets the number of tables. - SQLResultSetTable[] list = selectClause.tableList; - int size = list.length; - - if (size == 1) // size == 1 is not a join. - return; - - SQLResultSetField[] fields = whereClause != null? whereClause.fieldList : null; - int i = size, - j, - startedIndexAux, - highest; - SQLResultSetTable rsTableAux1, - rsTableAux2; - Table table1, - table2; - int[] startedIndex = new int[size]; - int[] changedTo = new int[size]; - - // Starts the weight of the where clause expression tree. - while (--i >= 0) - { - list[i].table.weight = 0; - startedIndex[i] = changedTo[i] = i; - } - if (whereClause != null) - whereClause.expressionTree.weightTheTree(); - - i = size; - while (--i >= 0) // Reorders the tables according to the weight. - { - highest = -1; - table1 = (rsTableAux1 = list[j = i]).table; - - while (--j >= 0) - // juliana@238_2: improved join table reordering. - // Takes the table size into consideration. - if (table1.weight > (table2 = (rsTableAux2 = list[j]).table).weight - || (table1.weight == table2.weight && table1.db.rowCount > table2.db.rowCount)) - { - rsTableAux1 = rsTableAux2; - highest = j; - } - - if (highest != -1) // Changes table order. - { - list[highest] = list[i]; - list[i] = rsTableAux1; - changedTo[startedIndex[highest]] = i; - changedTo[startedIndexAux = startedIndex[i]] = highest; - startedIndex[i] = startedIndex[highest]; - startedIndex[highest] = startedIndexAux; - } - } - - if (whereClause != null) // Rearranges the indexRs of the where clause fieldlist. - { - i = whereClause.fieldsCount; - while (-- i >= 0) - fields[i].indexRs = changedTo[fields[i].indexRs]; - } - } - - // juliana@212_4 - - /** - * Binds the SQLSelectStatement to the select clause tables. - * - ** @param driver The Litebase connection. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private void bindSelectStatement(LitebaseConnection driver) throws InvalidDateException, InvalidNumberException - { - // First thing to do is to bind the columns in all clauses. - SQLSelectClause select = selectClause; - IntHashtable names2Index = select.htName2index; - SQLResultSetTable[] listRsTables = select.tableList; - int i = listRsTables.length, - j = 0, - totalLen = 0; - Table table; - - while (--i >= 0) - totalLen += listRsTables[i].table.columnCount; - - byte[] types = new byte[totalLen]; - - i = listRsTables.length; - while (--i >= 0) // Adds the column types properties of all tables to a big array. - { - table = listRsTables[i].table; - Vm.arrayCopy(table.columnTypes, 0, types, j, table.columnCount); - j += table.columnCount; - } - - // Binds the SQL Clauses. Note: The HAVING clause will have a late binding. - select.bindColumnsSQLSelectClause(driver); // Binds the select clause. - if (whereClause != null) // Binds the where clause if it exists. - whereClause.bindColumnsSQLBooleanClause(names2Index, types, select.tableList); - if (groupByClause != null) // Binds the group by clause if it exists. - groupByClause.bindColumnsSQLColumnListClause(names2Index, types, select.tableList); - if (orderByClause != null) // Binds the order by clause if it exists. - orderByClause.bindColumnsSQLColumnListClause(names2Index, types, select.tableList); - } - - /** - * Validates the SQLSelectStatement. - * - * @throws SQLParseException If a query with group by is not well-formed, if there is a having clause without an aggregation, a field in the - * having clause is not in the select clause, there is no order by and there are aggregated functions mixed with real columns, or there is - * an aggregation with an order by clause and no group by clause. - */ - private void validateSelectStatement() throws SQLParseException - { - SQLColumnListClause groupBy = groupByClause, - orderBy = orderByClause; - SQLSelectClause select = selectClause; - - // Checks if the order by and group by clauses matchs. - if (groupBy != null && orderBy != null) - groupBy.checkEquality(orderBy); - - int selectCount = select.fieldsCount, - i = selectCount, - j; - SQLResultSetField field1, - field2; - SQLResultSetField[] selectFields = select.fieldList; - - if (groupBy != null) // Validates the group by clause if it exists. - { - SQLBooleanClause having = havingClause; - - while (--i >= 0) - { - field1 = selectFields[i]; - - // For now, there is no support for queries with GROUP BY and virtual columns in the SELECT clause that are not aggregated functions. - if (!field1.isAggregatedFunction && field1.isVirtual) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_VIRTUAL_COLUMN_ON_GROUPBY)); - - // Checks if every non-aggregated function field that is listed in the SELECT clause is listed in the GROUP BY clause. - if (!field1.isAggregatedFunction) - groupBy.sqlcolumnlistclauseContains(field1.tableColIndex); - } - - // Checks if all fields referenced in the HAVING clause are listed as aliased aggregated functions in the SELECT clause. - if (having != null) - { - i = having.fieldsCount; - SQLResultSetField[] havingFields = having.fieldList; - boolean found; - - while (--i >= 0) - { - field1 = havingFields[i]; - found = false; - j = selectCount; - - while (--j >= 0) - { - if (field1.aliasHashCode == (field2 = selectFields[j]).aliasHashCode) - { - if (field2.isAggregatedFunction) - { - found = true; - break; - } - - // It is not an aggregated function. Throws an exception. - throw new SQLParseException(field1.alias + LitebaseMessage.getMessage(LitebaseMessage.ERR_IS_NOT_AGGREG_FUNCTION)); - } - } - - if (!found) // The having clause fields must be in the select clause fields. - throw new SQLParseException(field1.alias + LitebaseMessage.getMessage(LitebaseMessage.ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION)); - } - } - } - else - { - // If there is no 'GROUP BY' clause, there can not be aggregated functions mixed with real columns in the SELECT clause. - if (select.hasRealColumns && select.hasAggFunctions) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANNOT_MIX_AGGREG_FUNCTION)); - - // If there are aggregate functions with an ORDER BY clause with no GROUP BY clause, also raises an exception. - if (select.hasAggFunctions && orderBy != null) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY)); - } - } - - /** - * Generates a table to store the result set. - * - * @param driver The connection with Litebase. - * @return The temporary result set table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private Table generateResultSetTable(LitebaseConnection driver) throws IOException, InvalidDateException, InvalidNumberException - { - ResultSet[] listRsTemp; - SQLSelectClause select = selectClause; - SQLColumnListClause orderBy = orderByClause, - groupBy = groupByClause; - SQLBooleanClause where = whereClause, - having = havingClause; - SQLResultSetTable[] tableList = select.tableList; - SQLResultSetField[] fieldList = select.fieldList; - SQLColumnListClause sortListClause = orderBy == null? groupBy : orderBy; - int numTables = tableList.length, - i = numTables, - count = sortListClause != null? sortListClause.fieldsCount : 0, - totalRecords, - selectFieldsCount = select.fieldsCount; - Table tableOrig = null; - - if (numTables == 1) // The query is not a join. - (tableOrig = tableList[0].table).answerCount = -1; - - // Optimization for queries that just wants to count the number of records of a table ("SELECT COUNT(*) FROM TABLE"). - boolean countQueryWithWhere = false; - String countAlias = null; - - // juliana@226_13: corrected a bug where a query of the form "select year(field) as years from table" would be confunded with - // "select count(*) as years from table". - if (selectFieldsCount == 1 && groupBy == null && fieldList[0].sqlFunction == SQLElement.FUNCTION_AGG_COUNT && fieldList[0].isAggregatedFunction) - { - countAlias = fieldList[0].alias; - if (where == null) - { - totalRecords = 1; - while (--i >= 0) - { - tableOrig = tableList[i].table; - totalRecords *= tableOrig.db.rowCount - tableOrig.deletedRowsCount; - } - return createIntValueTable(driver, totalRecords, countAlias); - } - countQueryWithWhere = true; - } - - // Gathers meta data for the temporary table that will store the result set. - byte[] columnTypes = new byte[count + selectFieldsCount]; - short[] columnIndexes = new short[count + selectFieldsCount]; - int[] columnHashes = new int[count + selectFieldsCount]; - int[] columnSizes = new int[count + selectFieldsCount]; - Table[] columnIndexesTables = new Table[count + selectFieldsCount]; - int size = 0; - - SQLResultSetField field, - param; - ResultSet rsTemp = null; - - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - IntHashtable colHashesTable = new IntHashtable(selectFieldsCount); // The hash table of the columns. - - // Maps the aggregated function parameter column indexes to the aggregate function code. - IntHashtable aggFunctionsTable = new IntHashtable(selectFieldsCount); - - i = -1; - while (++i < selectFieldsCount) - { - columnSizes[size] = (field = fieldList[i]).size; - - // Decides which hash code to use as the column name in the temporary table and which data type to assign to the temporary table. - if (field.isVirtual) - { - if (field.isAggregatedFunction) - { - // Finds the fields that are parameter for a MAX() and MIN() function that can use an index. - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - if (field.sqlFunction == SQLElement.FUNCTION_AGG_MAX || field.sqlFunction == SQLElement.FUNCTION_AGG_MIN) - field.findMaxMinIndex(); - - aggFunctionsTable.put(i, field.sqlFunction); - } - if ((param = field.parameter) == null) - { - columnHashes[size] = field.aliasHashCode; // Uses the alias hash code instead. - - // This is just a place holder, since this column does not map to any column in the database. - columnIndexes[size] = (short)-1; - columnIndexesTables[size] = null; - - columnTypes[size++] = (byte)field.dataType; // Uses the field data type. - } - else - { - // Uses the parameter hash and data type. - columnTypes[size] = (byte)param.dataType; - columnHashes[size] = field.aliasHashCode; - columnIndexes[size] = (short)param.tableColIndex; - columnIndexesTables[size++] = field.table; - - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - colHashesTable.put(param.aliasHashCode, 1); // juliana@253_1: corrected a bug when sorting if the sort field is in a function. - } - } - else - { - // A real column was selected. - columnTypes[size] = (byte)field.dataType; - columnHashes[size] = field.tableColHashCode; - columnIndexes[size] = (short)field.tableColIndex; - columnIndexesTables[size++] = field.table; - - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - colHashesTable.put(field.aliasHashCode, 0); // juliana@253_1: corrected a bug when sorting if the sort field is in a function. - colHashesTable.put(field.tableColHashCode, 0); - } - } - - if (sortListClause != null) - { - sortListClause.findSortIndex(); // juliana@230_29: order by and group by now use indices on simple queries. - - // Checks if all columns listed in the order by/group by clause were selected. If not, includes the ones that are missing. - // It must be remembered that, if both present, group by and order by must match. So, it does not matter which one is picked. - - fieldList = sortListClause.fieldList; - - i = -1; - while (++i < count) - { - // juliana@253_1: corrected a sort causing AOOIBE if the sort field is in a function. - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - if (colHashesTable.get((field = fieldList[i]).aliasHashCode, -1) == 0) - continue; - - // The sorting column is missing. Adds it to the temporary table. - columnTypes[size] = (byte)field.dataType; - columnSizes[size] = field.size; - columnHashes[size] = field.tableColHashCode; - columnIndexesTables[size] = field.table; - columnIndexes[size++] = (short)field.tableColIndex; - } - } - - // Creates the temporary table to store records that satisfy the WHERE clause. - Table tempTable = null; - totalRecords = 0; - boolean useIndex = true; - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - // For optimization, the first temporary table will NOT be created, in case there is no WHERE clause and sort clause (either ORDER BY or GROUP - // BY) and the SELECT clause contains aggregated functions. In this case calculation of the aggregated functions will be made on the existing - // table. - if (where == null && sortListClause == null && select.hasAggFunctions && numTables == 1) - { - // In this case, there is no need to create the temporary table. Just points to the necessary structures of the original table. - totalRecords = (tempTable = tableOrig).db.rowCount; - - // The index should not be used for MAX() and MIN() if not all the fields are MAX() and MIN() or one of the parametes cannot use an index. - i = -1; - while (++i < selectFieldsCount) - if (!(field = fieldList[i]).isAggregatedFunction || field.index < 0 - || (field.sqlFunction != SQLElement.FUNCTION_AGG_MAX && field.sqlFunction != SQLElement.FUNCTION_AGG_MIN)) - { - useIndex = false; - break; - } - } - else - { - // Creates a result set from the table, using the current WHERE clause applying the table indexes. - rsTemp = (listRsTemp = createListResultSetForSelect(tableList, where))[0]; - - // The index should not be used for MAX() and MIN() if there is a join, a sort or the indices do not resolve all the query. - if (sortListClause == null && (where == null || where.expressionTree == null) && numTables == 1) - { - // The index should not be used for MAX() and MIN() if not all the fields are MAX() and MIN() or one of the parametes cannot use an - // index. - i = -1; - while (++i < selectFieldsCount) - if (!(field = fieldList[i]).isAggregatedFunction || field.index < 0 - || (field.sqlFunction != SQLElement.FUNCTION_AGG_MAX && field.sqlFunction != SQLElement.FUNCTION_AGG_MIN)) - { - useIndex = false; - break; - } - } - else - useIndex = false; - - // juliana@230_29: order by and group by now use indices on simple queries. - // Only uses index when sorting if all the indices are applied. - if (sortListClause != null && ((where != null && where.expressionTree != null) || select.hasAggFunctions || numTables != 1)) - sortListClause.index = -1; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if ((sortListClause != null && sortListClause.index == -1) || countQueryWithWhere || numTables != 1) - { - // Optimization for queries of type "SELECT COUNT(*) FROM TABLE WHERE..." Just counts the records of the result set and writes it to a - // table. - if (countQueryWithWhere && numTables == 1) - { - where.sqlBooleanClausePreVerify(); - rsTemp.pos = -1; - while (rsTemp.getNextRecord()) - totalRecords++; - if (rsTemp.table.name == null) - rsTemp.table.db = null; - return createIntValueTable(driver, totalRecords, countAlias); - } - - // Creates the temporary table to store the result set records. - // Not creating a new array for hashes means BUM! - tempTable = driver.driverCreateTable(null, null, duplicateIntArray(columnHashes, size), duplicateByteArray(columnTypes, size), - duplicateIntArray(columnSizes, size), null, null, Utils.NO_PRIMARY_KEY, Utils.NO_PRIMARY_KEY, null); - - // Writes the result set records to the temporary table. - totalRecords = tempTable.writeResultSetToTable(listRsTemp, duplicateShortArray(columnIndexes, size), selectClause, columnIndexesTables, - where != null ? where.type : -1); - - if (select.type == SQLSelectClause.COUNT_WITH_WHERE) - return createIntValueTable(driver, totalRecords, countAlias); - - if (totalRecords == 0) // No records retrieved. Exit. - return tempTable; - } - - // A query that use index for MAX() and MIN() should not check now which rows are answered. - else if (useIndex) - tempTable = tableOrig; - else if (sortListClause == null) - { - byte[] allRowsBitmap = tableOrig.allRowsBitmap; - int newLength = (tableOrig.db.rowCount + 7) >> 3, - oldLength = allRowsBitmap == null? -1 : allRowsBitmap.length; - - if (newLength > oldLength) - tableOrig.allRowsBitmap = allRowsBitmap = new byte[newLength]; - else - Convert.fill(allRowsBitmap, 0, oldLength, 0); - computeAnswer(rsTemp); - - if (!select.hasAggFunctions) - return tableOrig; - else - totalRecords = (tempTable = tableOrig).answerCount; - } - } - - // juliana@230_29: order by and group by now use indices on simple queries. - if (sortListClause != null) // Sorts the temporary table, if required. - { - if (sortListClause.index == -1) - tempTable.sortTable(groupBy, orderBy, driver); // juliana@220_3 - else - { - tempTable = driver.driverCreateTable(null, null, duplicateIntArray(columnHashes, size), duplicateByteArray(columnTypes, size), - duplicateIntArray(columnSizes, size), null, null, Utils.NO_PRIMARY_KEY, Utils.NO_PRIMARY_KEY, null); - - PlainDB plainDB = tempTable.db; - SQLValue[] record = SQLValue.newSQLValues(tempTable.columnCount); - Index index; - - plainDB.rowAvail = (rsTemp.rowsBitmap == null? tableOrig.db.rowCount - tableOrig.deletedRowsCount : - Utils.countBits(rsTemp.rowsBitmap.items)); - plainDB.db.growTo(plainDB.rowAvail++ * plainDB.rowSize); - - if (sortListClause.isComposed) - index = tableOrig.composedIndices[sortListClause.index].index; - else - index = tableOrig.columnIndices[sortListClause.index]; - if (sortListClause.fieldList[0].isAscending) - index.sortRecordsAsc(rsTemp.rowsBitmap, tempTable, record, duplicateShortArray(columnIndexes, size), selectClause); - else - index.sortRecordsDesc(rsTemp.rowsBitmap, tempTable, record, duplicateShortArray(columnIndexes, size), selectClause); - if ((totalRecords = plainDB.rowCount) == 0) - return tempTable; - if (groupBy != null) - groupBy.bindColumnsSQLColumnListClause(tempTable.htName2index, tempTable.columnTypes, null); - } - } - // There is still one new temporary table to be created, if: - // 1) The select clause has aggregate functions, or - // 2) There is a group by clause. - if (!select.hasAggFunctions && groupBy == null) - return tempTable; - - // When creating the new temporary table, removes the extra fields that were created to perform the sort. - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - if (sortListClause != null && (count = tempTable.columnCount) != selectFieldsCount) - size = selectFieldsCount; - - // Also updates the types and hashcodes to reflect the types and aliases of the final temporary table, since they may still reflect the - // aggregated functions parameter list types and hashcodes. - byte[] columnTypesItems = duplicateByteArray(columnTypes, size); // Not creating a new array means BUM! - int[] columnSizesItems = duplicateIntArray(columnSizes, size); - int[] columnHashesItems = duplicateIntArray(columnHashes, size); - - // First preserves the original types, since they will be needed in the aggregated functions running totals calculation. - byte[] origColumnTypesItems = tempTable.columnTypes; - - fieldList = select.fieldList; - i = selectFieldsCount; - - while (--i >= 0) - { - columnTypesItems[i] = (byte)(field = fieldList[i]).dataType; - columnSizesItems[i] = field.size; - columnHashesItems[i] = field.aliasHashCode; - } - - // Creates the second temporary table. - Table tempTable2 = driver.driverCreateTable(null, null, columnHashesItems, columnTypesItems, columnSizesItems, null, null, - Utils.NO_PRIMARY_KEY, Utils.NO_PRIMARY_KEY, null); - - count = totalRecords; // Starts writing the records from the first temporary table into the second temporary table. - - // Aggregates functions local variables. - SQLValue[] aggFunctionsRunTotals = null; - int[] aggFunctionsParamCols = null; - int[] aggFunctionsRealParamCols = null; - int[] aggFunctionsCodes = null; - int[] paramCols = null; - - int aggFunctionsColsCount = 0, - groupCount = 0; // Variable to count how many records are currently listed in the group. - boolean aggFunctionExist = select.hasAggFunctions; // Initializes the aggregated functions running totals. - - if (aggFunctionExist && !useIndex) - { - aggFunctionsRunTotals = SQLValue.newSQLValues(selectFieldsCount); - aggFunctionsParamCols = aggFunctionsTable.getKeys().items; - aggFunctionsColsCount = aggFunctionsParamCols.length; - aggFunctionsCodes = aggFunctionsTable.getValues().items; - - // If the temporary table points to a real table, stores also the column indexes of aggregate functions parameter list from the real table. - if (tempTable.name != null) - { - aggFunctionsRealParamCols = new int[aggFunctionsColsCount]; - i = aggFunctionsColsCount; - while (--i >= 0) - { - param = fieldList[aggFunctionsParamCols[i]].parameter; - aggFunctionsRealParamCols[i] = (param == null? -1 : param.tableColIndex); - } - } - } - - SQLValue[] record1 = SQLValue.newSQLValues((count = tempTable.columnCount) > selectFieldsCount? count : selectFieldsCount); - SQLValue[] curRecord = record1; - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - if (useIndex) - { - // No rows in the answer. - if (tableOrig.db.rowCount - tableOrig.deletedRowsCount == 0 || (where != null && Utils.countBits(rsTemp.rowsBitmap.items) == 0)) - return tempTable2; - - Index index; - byte[] nulls = tempTable2.columnNulls[0]; - IntVector rowsBitmap = (rsTemp == null? null : rsTemp.rowsBitmap); - - // Computes the MAX() and MIN() for all the fields. - i = -1; - curRecord[0].isNull = true; // No rows yet. - while (++i < selectFieldsCount) - { - if ((field = fieldList[i]).isComposed) - index = tableOrig.composedIndices[field.index].index; - else - index = tableOrig.columnIndices[field.index]; - if (field.sqlFunction == SQLElement.FUNCTION_AGG_MAX) - index.findMaxValue(curRecord[i], rowsBitmap); - else - index.findMinValue(curRecord[i], rowsBitmap); - } - if (curRecord[0].isNull) // No rows found: returns an empty table. - return tempTable2; - Convert.fill(nulls, 0, nulls.length, 0); - tempTable2.writeRSRecord(curRecord); - return tempTable2; - } - - SQLValue[] record2 = SQLValue.newSQLValues(record1.length); - SQLValue[] prevRecord = null; - byte[] nullsPrevRecord = tempTable.columnNulls[0]; - byte[] nullsCurRecord = tempTable.columnNulls[1]; - boolean writeDelayed = aggFunctionExist || groupByClause != null; - - // Loops through the records of the temporary table, to calculate agregated values and/or write the group records. - boolean isTableTemporary = tempTable.name == null; - paramCols = (isTableTemporary? aggFunctionsParamCols : aggFunctionsRealParamCols); - - int[] groupCountCols = new int[aggFunctionsColsCount]; // Each column has a groupCount because of the null values. - byte[] allRowsBitMap = tempTable.allRowsBitmap; - int j, - colIndex, - sqlAggFunction, - colType, - row = -1, - numberRows = tempTable.db.rowCount, - answerCount = tempTable.answerCount; - SQLValue aggValue, - value; - - for (i = -1; ++i < totalRecords; groupCount++) - { - if (answerCount >= 0) - { - while (row++ < numberRows && (allRowsBitMap[row >> 3] & (1 << (row & 7))) == 0); - tempTable.readRecord(curRecord, row, 1, driver, null, true, null); - } - else - tempTable.readRecord(curRecord, i, 1, driver, null, true, null); // juliana@220_3 juliana@227_20 - if (!isTableTemporary && !tempTable.db.recordNotDeleted()) // Because it is possible to be pointing to a real table, skips deleted records. - { - groupCount--; - continue; - } - - // In case there is a group by, checks if there was a change in the group record composition. - if (groupBy != null && groupCount != 0 - && Utils.compareRecords(prevRecord, curRecord, nullsPrevRecord, nullsCurRecord, groupBy.fieldList) != 0) - { - if (aggFunctionExist) // Checks if there are aggregate functions. - - // Concludes any aggregated function calculation. - endAggFunctionsCalc(prevRecord, groupCount, aggFunctionsRunTotals, aggFunctionsCodes, aggFunctionsParamCols, paramCols, - aggFunctionsColsCount, origColumnTypesItems, groupCountCols); - - // Flushs the previous record and starts a new group counting. - // Takes the null values for the non-aggregate fields into consideration. - tempTable2.columnNulls[0] = nullsPrevRecord; - j = aggFunctionsColsCount; - while (--j >= 0) - { - Utils.setBit(tempTable2.columnNulls[0], aggFunctionsParamCols[j], groupCountCols[j] == 0); - groupCountCols[j] = 0; - } - tempTable2.writeRSRecord(prevRecord); - groupCount = 0; - } - - if (aggFunctionExist) // Checks if there are aggregate functions. - { - // Performs the calculation of the aggregate functions. - j = aggFunctionsColsCount; - while (--j >= 0) - { - // Increments the count(*). NOTE: in the future, implementation of count(field_name) needs verify the null values. - if (aggFunctionsCodes[j] == 0) - { - groupCountCols[j]++; - continue; - } - - if ((colIndex = paramCols[j]) < 0 || (nullsCurRecord[colIndex >> 3] & (1 << (colIndex & 7))) != 0) // The agg functions skip nulls. - continue; - - groupCountCols[j]++; - sqlAggFunction = aggFunctionsCodes[j]; - colType = origColumnTypesItems[colIndex]; - - aggValue = aggFunctionsRunTotals[j]; - value = curRecord[colIndex]; - - switch (sqlAggFunction) - { - case SQLElement.FUNCTION_AGG_AVG: - case SQLElement.FUNCTION_AGG_SUM: - { - switch (colType) // Checks the type of the column. - { - case SQLElement.SHORT: - aggValue.asDouble += value.asShort; - break; - - case SQLElement.INT: - aggValue.asDouble += value.asInt; - break; - - case SQLElement.LONG: - aggValue.asDouble += value.asLong; - break; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - aggValue.asDouble += value.asDouble; - break; - - // AVG and SUM can't be used with date and datetime. - case SQLElement.DATE: // rnovais@567_2 - case SQLElement.DATETIME: - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_SUM_AVG_WITH_DATE_DATETIME)); - } - - break; - } - - // juliana@226_5: the aggregation functions MAX() and MIN() now work for CHAR, VARCHAR, CHAR NOCASE, and VARCHAR NOCASE column - // types. - case SQLElement.FUNCTION_AGG_MAX: - { - switch (colType) // Checks the type of the column - { - case SQLElement.CHARS: // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - if (groupCountCols[j] == 1 || aggValue.asString.compareTo(value.asString) < 0) - { - aggValue.asString = value.asString; - aggValue.asInt = value.asInt; - aggValue.asLong = value.asLong; - } - break; - - case SQLElement.SHORT: - if (groupCountCols[j] == 1 || aggValue.asShort < value.asShort) - aggValue.asShort = value.asShort; - break; - - case SQLElement.DATE: // rnovais@567_2 - case SQLElement.INT: - if (groupCountCols[j] == 1 || aggValue.asInt < value.asInt) - aggValue.asInt = value.asInt; - break; - - case SQLElement.LONG: - if (groupCountCols[j] == 1 || aggValue.asLong < value.asLong) - aggValue.asLong = value.asLong; - break; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - if (groupCountCols[j] == 1 || aggValue.asDouble < value.asDouble) - aggValue.asDouble = value.asDouble; - break; - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case SQLElement.CHARS_NOCASE: - String aggValueString = aggValue.asString.toLowerCase(), - valueString = value.asString.toLowerCase(); - if (groupCountCols[j] == 1 || aggValueString.compareTo(valueString) < 0) - { - aggValue.asString = value.asString; - aggValue.asInt = value.asInt; - aggValue.asLong = value.asLong; - } - break; - - // The date is smaller, or the time is smaller. - case SQLElement.DATETIME: // rnovais@567_2 - if (groupCountCols[j] == 1) - { - aggValue.asInt = value.asInt; - aggValue.asShort = value.asShort; - } - else - if (aggValue.asInt <= value.asInt && (aggValue.asInt < value.asInt || aggValue.asShort < value.asShort)) - { - aggValue.asInt = value.asInt; - aggValue.asShort = value.asShort; - } - } - break; - } - - // juliana@226_5: the aggregation functions MAX() and MIN() now work for CHAR, VARCHAR, CHAR NOCASE, and VARCHAR NOCASE column - // types. - case SQLElement.FUNCTION_AGG_MIN: - { - switch (colType) // Checks the type of the column - { - case SQLElement.CHARS: // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - if (groupCountCols[j] == 1 || aggValue.asString.compareTo(value.asString) > 0) - { - aggValue.asString = value.asString; - aggValue.asInt = value.asInt; - aggValue.asLong = value.asLong; - } - break; - - case SQLElement.SHORT: - if (groupCountCols[j] == 1 || aggValue.asShort > value.asShort) - aggValue.asShort = value.asShort; - break; - - case SQLElement.DATE: // rnovais@_567_2 - case SQLElement.INT: - if (groupCountCols[j] == 1 || aggValue.asInt > value.asInt) - aggValue.asInt = value.asInt; - break; - - case SQLElement.LONG: - if (groupCountCols[j] == 1 || aggValue.asLong > value.asLong) - aggValue.asLong = value.asLong; - break; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - if (groupCountCols[j] == 1 || aggValue.asDouble > value.asDouble) - aggValue.asDouble = value.asDouble; - break; - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case SQLElement.CHARS_NOCASE: - String aggValueString = aggValue.asString.toLowerCase(), - valueString = value.asString.toLowerCase(); - if (groupCountCols[j] == 1 || aggValueString.compareTo(valueString) > 0) - { - aggValue.asString = value.asString; - aggValue.asInt = value.asInt; - aggValue.asLong = value.asLong; - } - break; - - case SQLElement.DATETIME: // rnovais@567_2 - if (groupCountCols[j] == 1) - { - aggValue.asInt = value.asInt; - aggValue.asShort = value.asShort; - } - else - if (aggValue.asInt >= value.asInt && (aggValue.asInt > value.asInt || aggValue.asShort > value.asShort)) // the date is greater, or the time is greater - { - aggValue.asInt = value.asInt; - aggValue.asShort = value.asShort; - } - } - } - } - } - } - if (!writeDelayed) - { - tempTable2.columnNulls[0] = nullsCurRecord; - tempTable2.writeRSRecord(curRecord); - } - - Vm.arrayCopy(nullsCurRecord, 0, nullsPrevRecord, 0, nullsCurRecord.length); - curRecord = ((prevRecord = curRecord) == record1)? record2 : record1; - } - - if (writeDelayed && groupCount > 0) // If there was delayed writing, flushs the last record. - { - if (aggFunctionExist) - // Concludes any aggregated function calculation. - endAggFunctionsCalc(prevRecord, groupCount, aggFunctionsRunTotals, aggFunctionsCodes, aggFunctionsParamCols, paramCols, - aggFunctionsColsCount, origColumnTypesItems, groupCountCols); - - tempTable2.columnNulls[0] = nullsCurRecord; // Takes the null values for the non-aggregate fields into consideration. - j = aggFunctionsColsCount; - while (--j >= 0) // Writes the last record. - { - Utils.setBit(tempTable2.columnNulls[0], aggFunctionsParamCols[j], groupCountCols[j] == 0); - groupCountCols[j] = 0; - } - tempTable2.writeRSRecord(prevRecord); - } - - if (having == null) // If there is no having clause, returns the second temorary table. - return tempTable2; - - // Creates the last temporary table to hold the records that comply to the "HAVING" clause. - // Creates the second temporary table. - Table tempTable3 = driver.driverCreateTable(null, null, tempTable2.columnHashes, tempTable2.columnTypes, tempTable2.columnSizes, null, null, - Utils.NO_PRIMARY_KEY, Utils.NO_PRIMARY_KEY, null); - - // The HAVING clause only works with aliases. So, remaps the table column names to use the aliases of the select statement, instead of the - // original column names. - tempTable3.remapColumnsNames2Aliases(select.fieldList); - - // Binds the HAVING clause to the table columns. - having.bindColumnsSQLBooleanClause(tempTable3.htName2index, tempTable3.columnTypes, (SQLResultSetTable[])null); - - rsTemp = tempTable2.createResultSetForSelect(having); // Creates a result set from the table, using the HAVING clause - rsTemp.driver = driver; - - // Writes the result set to the third temporary table. - tempTable3.writeResultSetToTable(new ResultSet[] {rsTemp}, null, select, null, -1); - return tempTable3; - } - - // Included the whereClause parameter. - // Renamed from createResultSet to createResultSetForSelect - /** - * Creates a list of result sets for each table of the select clause. - * - * @param tableList The tableList of the select clause. - * @param whereClause The condition of the query. - * @return A list of result sets. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private ResultSet[] createListResultSetForSelect(SQLResultSetTable[] tableList, SQLBooleanClause whereClause) throws IOException, InvalidDateException - { - int size = tableList.length, // The table list size. - i = size; - ResultSet[] rsList = new ResultSet[size]; // The result set list. - boolean hasComposedIndex = false; // Indicates if there is a composed index. - - while (--i >= 0) - { - Table t = tableList[i].table; - - // Apply table indexes, if any. - ResultSet rs = rsList[i] = t.createResultSet(whereClause); - rs.columnCount = t.columnCount; // Sets the column count. - rs.indexRs = i; // Sets the table index. - - if (!hasComposedIndex) // It is only necessary to have one table with composed indices. - hasComposedIndex = t.numberComposedIndices > 0; - } - - if (whereClause != null) // Tries to apply the table indices to generate a bitmap of the rows to be returned. - { - if (size > 1) // Join. - whereClause.expressionTree.setIndexRsOnTree(); - generateIndexedRowsMap(rsList, hasComposedIndex); - } - - return rsList; - } - - /** - * Generates an index bit map for a list of result sets. - * - * @param rsList The list of result sets. - * @param hasComposedIndex Indicates if the table has a composed index. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - static void generateIndexedRowsMap(ResultSet[] rsList, boolean hasComposedIndex) throws IOException, InvalidDateException - { - SQLBooleanClause whereClause = rsList[0].whereClause; - - if (rsList.length > 1) // Applies the table indexes to the where clause. If it was not possible, return. - { - if (!whereClause.sqlbooleanclauseApplyTableIndexesJoin()) - return; - } - else if (!whereClause.sqlbooleanclauseApplyTableIndices(rsList[0].table.columnIndices, hasComposedIndex)) - return; - - computeIndex(rsList, rsList.length > 1, -1, null, -1, -1); - - if (whereClause.expressionTree == null) // There is no where clause left, since all rows can be returned using the indexes. - { - int i = rsList.length; - while (--i >= 0) - rsList[i].whereClause = null; - } - - } - - /** - * Finds the rows that satisfy the query clause using the indices. - * - * @param rsList The result set list, one for each table. - * @param isJoin Indicates that the query has a join. - * @param indexRsOnTheFly The index of the result set or -1 if the query is being indexed on the fly. - * @param value The value to be indexed on the fly. - * @param operator The operand type. Used only to index on the fly. - * @param colIndex The index column. Used only to index on the fly. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - static void computeIndex(ResultSet[] rsList, boolean isJoin, int indexRsOnTheFly, SQLValue value, int operator, int colIndex) - throws IOException, InvalidDateException - { - MarkBits markBits = new MarkBits(); - int[] items; - boolean onTheFly = indexRsOnTheFly != -1; - int count = 1, - booleanOp, - i = -1, - j, - size = rsList.length; - SQLBooleanClause whereClause = rsList[0].whereClause; - byte[] indexedCols = whereClause.appliedIndexesCols; - byte[] relationalOps = whereClause.appliedIndexesRelOps; - ResultSet[] rsListPointer; // The resulting indexed row bitmap. - Table[] appliedIndexesTables = whereClause.appliedIndexesTables; - SQLBooleanClauseTree[] indexedValues = whereClause.appliedIndexesValueTree; - ComposedIndex[] appliedComposedIndexes = whereClause.appliedComposedIndexes; - - // Gets the list of indexes that were applied to the where clause, together - // with the indexes values to search for and the boolean operation to apply. - if (onTheFly) - booleanOp = rsList[indexRsOnTheFly].rowsBitmapBoolOp; - else - { - count = whereClause.appliedIndexesCount; - booleanOp = whereClause.appliedIndexesBooleanOp; - } - - rsListPointer = new ResultSet[count]; - if (isJoin) // Puts the result set bag in order with the tables. - { - i = count; - while (--i >= 0) - { - j = size; - while (--j >= 0) - if (rsList[j].table.equals(appliedIndexesTables[i])) - { - rsListPointer[i] = rsList[j]; - break; - } - } - } - - // Loops through all applied indexes and records the indexed rows in the bitmap. - ResultSet rsBag = rsList[onTheFly? indexRsOnTheFly : 0]; - - Table table = rsBag.table; - int col, - op, - realOp, - idx; - boolean isComposed; - int[] ops = null; - SQLValue[] leftVal = new SQLValue[1]; - Index index; - - rsBag.indexCount = 0; - while (++i < count) - { - if (isJoin) - { - table = appliedIndexesTables[i]; - rsBag = rsListPointer[i]; - } - rsBag.indexCount++; - isComposed = appliedComposedIndexes[i] != null; - col = op = -1; - size = 1; - if (onTheFly) - { - col = colIndex; - op = operator; - leftVal[0] = value; - } - else - if (isComposed) - { - if (leftVal.length < (size = appliedComposedIndexes[i].columns.length)) // Reuses the buffer. - leftVal = new SQLValue[size]; - if (ops == null || ops.length < size) // Reuses the buffer. - ops = new int[size]; - for (int m = 0, k = i; m < size; m++, k++) - { - leftVal[m] = indexedValues[k].getOperandValue(); - ops[m] = relationalOps[k]; - } - } - else - { - leftVal[0] = indexedValues[i].getOperandValue(); - col = indexedCols[i]; - op = relationalOps[i]; - } - - index = isComposed? appliedComposedIndexes[i].index : table.columnIndices[col]; - - markBits.reset(index, table.db.rowCount); // Prepares the index row bitmap. - items = markBits.indexBitmap.items; - - j = size; - while (--j >= 0) - { - if (isComposed) - op = ops[j]; - idx = i + j; - - // If operation is 'like x%', then replaces the operand by the value without the % mask. - // juliana@230_10: solved a bug that could crash the application when more than one index is applied. - if (op == SQLElement.OP_PAT_MATCH_NOT_LIKE || op == SQLElement.OP_PAT_MATCH_LIKE) - leftVal[j].asString = (index.types[j] == SQLElement.CHARS_NOCASE)? indexedValues[i].strToMatch.toLowerCase() - : indexedValues[i].strToMatch; - - else // Checks if this is a "between" operation. - if (booleanOp == SQLElement.OP_BOOLEAN_AND && i < count - 1 && indexedCols[i + 1] == col - && (op == SQLElement.OP_REL_GREATER || op == SQLElement.OP_REL_GREATER_EQUAL) - && (relationalOps[idx + 1] == SQLElement.OP_REL_LESS || relationalOps[idx + 1] == SQLElement.OP_REL_LESS_EQUAL)) - { - markBits.rightKey = new Key(index); - - // Encapsulates. - markBits.rightKey.set(new SQLValue[] {indexedValues[idx + 1].getOperandValue()}); - markBits.rightOp[j] = relationalOps[idx + 1]; - - i++; // Next operation already processed. - } - else - { - realOp = op; - - switch (op) // When searching for !=, <, <=, opposite operation will be used instead. - { - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - realOp = SQLElement.OP_PAT_MATCH_LIKE; - break; - case SQLElement.OP_REL_LESS: - realOp = SQLElement.OP_REL_GREATER_EQUAL; - break; - case SQLElement.OP_REL_LESS_EQUAL: - realOp = SQLElement.OP_REL_GREATER; - break; - case SQLElement.OP_REL_DIFF: - realOp = SQLElement.OP_REL_EQUAL; - } - - if (op != realOp) // All rows will be marked and only the rows that satisfy the opposite operation will be reseted. - { - Convert.fill(items, 0, items.length, 0xFFFFFFFF); - markBits.bitValue = false; - op = realOp; - } - } - markBits.leftOp[j] = (byte)op; - } - markBits.leftKey.set(leftVal); - - switch (op) // Finally, marks all rows that match this value / range of values. - { - case SQLElement.OP_REL_EQUAL: - index.getValue(markBits.leftKey, markBits); - break; - case SQLElement.OP_PAT_MATCH_LIKE: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - index.getGreaterOrEqual(markBits); - } - - // If it is the first index, assigns the index bitmap to the resulting bitmap. Otherwise, merges the index bitmap with the existing bitmap, - // using the boolean operator. - if (rsBag.indexCount == 1) - { - if (onTheFly) - rsBag.auxRowsBitmap = markBits.indexBitmap; - else - rsBag.rowsBitmap = markBits.indexBitmap; - } - else - mergeBitmaps(rsBag.rowsBitmap.items, markBits.indexBitmap.items, booleanOp); - rsBag.rowsBitmapBoolOp = booleanOp; - if (isComposed) - i += size - 1; - } - } - - /** - * Merges two bitmaps into the first bitmap using the given boolean operator. - * - * @param bitmap1 The first bitmap. - * @param bitmap2 The second bitmap. - * @param booleanOp The boolean operator to be applied. - */ - static void mergeBitmaps(int[] bitmap1, int[] bitmap2, int booleanOp) - { - int i = bitmap1.length; - - if (booleanOp == SQLElement.OP_BOOLEAN_AND) - while (--i >= 0) - bitmap1[i] &= bitmap2[i]; - else - while (--i >= 0) - bitmap1[i] |= bitmap2[i]; - } - - /** - * Concludes the calculation of the given aggregated function running totals based on the given record and the group count. - * - * @param record The record that is the parameter for the aggregated function. - * @param groupCount The result of a COUNT(*). - * @param aggFunctionsRunTotals The results of the aggregated functions. - * @param aggFunctionsCodes The aggregated function codes. - * @param aggFunctionsParamCols The columns that are parameters to the aggregated functions. - * @param aggFunctionsRealParamCols The real columns that are parameters to the aggregated functions. - * @param aggFunctionsColsCount The number of columns that are parameters to the aggregated functions. - * @param columnTypes The types of the columns. - * @param groupCountCols The count for the groups. - */ - private void endAggFunctionsCalc(SQLValue[] record, int groupCount, SQLValue[] aggFunctionsRunTotals, int[] aggFunctionsCodes, - int[] aggFunctionsParamCols, int[] aggFunctionsRealParamCols, int aggFunctionsColsCount, byte[] columnTypes, int[] groupCountCols) - { - int j = aggFunctionsColsCount; - SQLValue aggValue, - value; - - // Concludes the calculation of the aggregate functions running totals. - while (--j >= 0) - { - aggValue = aggFunctionsRunTotals[j]; - value = record[aggFunctionsParamCols[j]]; - - switch (aggFunctionsCodes[j]) - { - case SQLElement.FUNCTION_AGG_AVG: - { - if (groupCountCols[j] != 0) - value.asDouble = aggValue.asDouble / groupCountCols[j]; - aggValue.asDouble = 0; - break; - } - - // juliana@226_5: the aggregation functions MAX() and MIN() now work for CHAR, VARCHAR, CHAR NOCASE, and VARCHAR NOCASE column types. - case SQLElement.FUNCTION_AGG_MAX: - case SQLElement.FUNCTION_AGG_MIN: - { - switch (columnTypes[aggFunctionsRealParamCols[j]]) // Checks the type of the column. - { - case SQLElement.SHORT: - value.asShort = aggValue.asShort; - break; - - // juliana@230_31: corrected a bug of MAX() and MIN() not working properly with DATE and DATETIME. - case SQLElement.DATETIME: - value.asShort = aggValue.asShort; - case SQLElement.INT: - case SQLElement.DATE: - value.asInt = aggValue.asInt; - break; - - case SQLElement.LONG: - value.asLong = aggValue.asLong; - break; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - value.asDouble = aggValue.asDouble; - break; - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - value.asString = aggValue.asString; - value.asInt = aggValue.asInt; - value.asLong = aggValue.asLong; - } - break; - } - - case SQLElement.FUNCTION_AGG_COUNT: - { - value.asInt = groupCount; - break; - } - - case SQLElement.FUNCTION_AGG_SUM: - { - value.asDouble = aggValue.asDouble; - aggValue.asDouble = 0; - } - } - } - } - - /** - * Creates a temporary table that stores only an integer value. - * - * @param driver The connection with Litebase. - * @param intValue The value to be put in the table. - * @param colName The column name of the single table column. - * @return The table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - Table createIntValueTable(LitebaseConnection driver, int intValue, String colName) throws IOException, InvalidDateException - { - SQLValue[] record = SQLValue.newSQLValues(1); - record[0].asInt = intValue; - - Table table = driver.driverCreateTable(null, null, new int[] {colName.hashCode()}, new byte[] {SQLElement.INT}, new int[1], null, null, - Utils.NO_PRIMARY_KEY, Utils.NO_PRIMARY_KEY, null); - table.writeRSRecord(record); - return table; - } - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - /** - * Calculates the answer of a select without aggregation, join, order by, or group by without using a temporary table. - * - * @param resultSet The result set of the table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private void computeAnswer(ResultSet resultSet) throws IOException, InvalidDateException, InvalidNumberException - { - int i; - Table table = resultSet.table; - - if (resultSet.whereClause == null && resultSet.rowsBitmap == null && table.deletedRowsCount == 0) - { - i = table.answerCount = table.db.rowCount; - while (--i >= 0) - Utils.setBit(table.allRowsBitmap, i, true); - } - else - { - i = 0; - while (resultSet.getNextRecord()) // No preverify needed. - { - Utils.setBit(table.allRowsBitmap, resultSet.pos, true); - i++; - } - table.answerCount = i; - } - } - - /** - * Returns a duplicated byte array. - * - * @param array The array to be duplicated. - * @param size The size of the array to be duplicated. - * @return An array of byte with all the elements. - */ - private byte[] duplicateByteArray(byte[] array, int size) // guich@554_34 - { - byte[] newArray = new byte[size]; - Vm.arrayCopy(array, 0, newArray, 0, size); - return newArray; - } - - /** - * Returns a duplicated short array. - * - * @param array The array to be duplicated. - * @param size The size of the array to be duplicated. - * @return An array of short with all the elements. - */ - private short[] duplicateShortArray(short[] array, int size) // guich@554_34 - { - short[] newArray = new short[size]; - Vm.arrayCopy(array, 0, newArray, 0, size); - return newArray; - } - - /** - * Returns a duplicated int array. - * - * @param array The array to be duplicated. - * @param size The size of the array to be duplicated. - * @return An array of int with all the elements. - */ - private int[] duplicateIntArray(int[] array, int size) // guich@554_34 - { - int[] newArray = new int[size]; - Vm.arrayCopy(array, 0, newArray, 0, size); - return newArray; - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLStatement.java b/LitebaseSDK/src/java/litebase/SQLStatement.java deleted file mode 100644 index 0911c8b463..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLStatement.java +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.sys.InvalidNumberException; -import totalcross.util.InvalidDateException; - -/** - * Internal use only. Abstract class for SQL statements. - */ -@Deprecated -abstract class SQLStatement -{ - /** - * The type of the statement, which can be one of: CMD_CREATE_TABLE, CMD_CREATE_INDEX, - * CMD_DROP_TABLE, CMD_DROP_INDEX, CMD_ALTER_DROP_PK, - * CMD_ALTER_ADD_PK, CMD_ALTER_RENAME_TABLE, CMD_ALTER_RENAME_COLUMN, - * CMD_SELECT, CMD_INSERT, CMD_UPDATE, or CMD_DELETE. - */ - int type; - - /** - * Sets a short value at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - */ - abstract void setParamValue(int index, short val); - - /** - * Sets an integer value at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - */ - abstract void setParamValue(int index, int val); - - /** - * Sets a long value at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - */ - abstract void setParamValue(int index, long val); - - /** - * Sets a float value at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - */ - abstract void setParamValue(int index, float val); - - /** - * Sets a double value at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - */ - abstract void setParamValue(int index, double val); - - /** - * Sets a string value at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - * @throws InvalidNumberException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - abstract void setParamValue(int index, String val) throws InvalidNumberException, InvalidDateException; - - /** - * Sets a byte array value (blob) at the parameter of the given index. - * - * @param index The index of the parameter. - * @param val The value of the parameter. - */ - abstract void setParamValue(int index, byte[] value); - - /** - * Sets null in a given field. - * - * @param index The index of the parameter value to be set as null, starting from 0. - */ - abstract void setNull(int index); - - /** - * Clears the parameter values. - */ - abstract void clearParamValues(); - - /** - * Checks if all the parameters values are defined. - */ - abstract void allParamValuesDefined(); -} diff --git a/LitebaseSDK/src/java/litebase/SQLUpdateStatement.java b/LitebaseSDK/src/java/litebase/SQLUpdateStatement.java deleted file mode 100644 index c3082df38c..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLUpdateStatement.java +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.InvalidDateException; - -/** - * Internal use only. Represents a SQL UPDATE statement. - */ -@Deprecated -class SQLUpdateStatement extends SQLStatement -{ - /** - * The base table used by the SQL expression. - */ - SQLResultSetTable rsTable; - - /** - * The where clause. - */ - SQLBooleanClause whereClause; - - /** - * The fields used to update a record. - */ - String[] fields; - - /** - * The record to be inserted. - */ - SQLValue[] record; - - /** - * The number of the parameters if the update statement is a preprared statement. - */ - int paramCount; - - /** - * The array with the indexes of the parameters. - */ - short[] paramIndexes; // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - - /** - * An array that indicates if a parameters is defined or not. - */ - boolean[] paramDefined; - - /** - * An array that indicates if a null value will be stored in a field. - */ - byte[] storeNulls; - - /** - * Constructs an update statement given the result of the parsing process. - * - * @param parser The result of the parsing process. - * @throws SQLParseException If there is a field named "rowid". - */ - SQLUpdateStatement(LitebaseParser parser) throws SQLParseException - { - int nValues = parser.fieldValuesSize; // Gets the values. - String value; - SQLBooleanClause clause = whereClause = parser.whereClause; - String[] fieldsAux = fields = new String[nValues]; // Create an array of fields. - - // Creates an array to store the fact that a value is null or not. - SQLValue[] recordAux = record = SQLValue.newSQLValues(nValues); - byte[] nulls = storeNulls = new byte[(nValues + 7) >> 3]; - - type = SQLElement.CMD_UPDATE; - rsTable = parser.tableList[0]; // Sets the result table. - - // Stores the fields. - Vm.arrayCopy(parser.fieldNames, 0, fields, 0, nValues); - - // Allocates space for the list of the parameters. Worst case: all fields are parameters. - // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - paramIndexes = new short[nValues]; - paramDefined = new boolean[nValues]; - - while (--nValues >= 0) - { - // juliana@230_40: rowid cannot be an update field. - if (fieldsAux[nValues].hashCode() == SQLElement.hcRowId) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_ROWID_CANNOT_BE_CHANGED)); - - value = parser.fieldValues[nValues]; - if (value != null) // Only stores values that are not null. - recordAux[nValues].asString = value; - else - Utils.setBit(nulls, nValues, recordAux[nValues].isNull = true); - } - - if (clause != null) // Process the where clause, if it exists. - { - // Compacts the resulting field list. - SQLResultSetField[] compactFieldList = new SQLResultSetField[clause.fieldsCount]; - Vm.arrayCopy(clause.fieldList, 0, compactFieldList, 0, clause.fieldsCount); - clause.fieldList = compactFieldList; - - // Compacts the parameter list. - SQLBooleanClauseTree[] compactParamList = new SQLBooleanClauseTree[clause.paramCount]; - Vm.arrayCopy(clause.paramList, 0, compactParamList, 0, clause.paramCount); - clause.paramList = compactParamList; - } - } - - /** - * Sets the value of a short parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the short parameter. - * @throws DriverException If the column type is not short. - */ - void setParamValue(int index, short val) throws DriverException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; // guich@lb225_1: masks out the sign bit in all reads of paramIndexes. - - if (rsTable.table.columnTypes[idx] != SQLElement.SHORT) // The type must be short. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - SQLValue value = record[idx]; - value.asShort = val; - paramDefined[index] = true; - Utils.setBit(storeNulls, idx, value.isNull = false); // The value is not null. - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - value.asString = null; - } - else // The parameter is in the where clause. - whereClause.paramList[index - paramCount].setParamValue(val); - } - - /** - * Sets the value of a integer parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the integer parameter. - * @throws DriverException If the column type is not int. - */ - void setParamValue(int index, int val) throws DriverException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; - - if (rsTable.table.columnTypes[idx] != SQLElement.INT) // The type must be int. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - SQLValue value = record[idx]; - value.asInt = val; - paramDefined[index] = true; - Utils.setBit(storeNulls, idx, value.isNull = false); // The value is not null. - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - value.asString = null; - } - else // The parameter is in the where clause. - whereClause.paramList[index - paramCount].setParamValue(val); - } - - /** - * Sets the value of a long parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the long parameter. - * @throws DriverException If the column type is not long. - */ - void setParamValue(int index, long val) throws DriverException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; - - if (rsTable.table.columnTypes[idx] != SQLElement.LONG) // The type must be long. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - SQLValue value = record[idx]; - value.asLong = val; - paramDefined[index] = true; - Utils.setBit(storeNulls, idx, value.isNull = false); // The value is not null. - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - value.asString = null; - } - else // The parameter is in the where clause. - whereClause.paramList[index - paramCount].setParamValue(val); - } - - /** - * Sets the value of a float parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the float parameter. - * @throws DriverException If the column type is not float. - */ - void setParamValue(int index, float val) throws DriverException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; - - if (rsTable.table.columnTypes[idx] != SQLElement.FLOAT) // The type must be float. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - SQLValue value = record[idx]; - value.asDouble = val; - paramDefined[index] = true; - Utils.setBit(storeNulls, idx, value.isNull = false); // The value is not null. - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - value.asString = null; - } - else // The parameter is in the where clause. - whereClause.paramList[index - paramCount].setParamValue(val); - } - - /** - * Sets the value of a double parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the double parameter. - * @throws DriverException If the column type is not double. - */ - void setParamValue(int index, double val) throws DriverException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; - - if (rsTable.table.columnTypes[idx] != SQLElement.DOUBLE) // The type must be double. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - SQLValue value = record[idx]; - value.asDouble = val; - paramDefined[index] = true; - Utils.setBit(storeNulls, idx, value.isNull = false); // The value is not null. - - // juliana@230_37: solved a possible bug when using prepared statements without issuing PreparedStatement.clearAllParameters(). - value.asString = null; - } - else // The parameter is in the where clause. - whereClause.paramList[index - paramCount].setParamValue(val); - } - - /** - * Sets the value of a string parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the string parameter. - * @throws DriverException If the column type is BLOB. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @throws InvalidNumberException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void setParamValue(int index, String val) throws DriverException, SQLParseException, InvalidNumberException, InvalidDateException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; - - if (rsTable.table.columnTypes[idx] == SQLElement.BLOB) // The type can't be a blob. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_STRING)); - - record[idx].asString = val; // Sets the values of the parameter in its list. - Utils.setBit(storeNulls, idx, record[idx].isNull = (val == null)); // Sets whether the value is or not null. - paramDefined[index] = true; - } - else // The parameter is in the where clause. - { - if (val == null) // A null can't be in a parameter of a where clause. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAM_NULL)); - whereClause.paramList[index - paramCount].setParamValue(val); - } - } - - /** - * Sets the value of a blob parameter at the given index. - * - * @param index The index of the parameter. - * @param val The value of the string parameter. - * @throws DriverException If the column type is not blob. - * @throws SQLParseException If the index is for the where clause. - */ - void setParamValue(int index, byte[] val) throws DriverException, SQLParseException - { - checkIndex(index); // Checks if the index is within the range. - - if (index >= paramCount) // // The parameter is in the where clause. - // Since blobs can't be in the were clause, an exception will be raised. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOB_WHERE)); - - // The parameter is in the update clause. - int idx = paramIndexes[index] & 0xFF; - - if (rsTable.table.columnTypes[idx] != SQLElement.BLOB) // The type must be blob. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES)); - - record[idx].asBlob = val; // Stores the value. - Utils.setBit(storeNulls, idx, record[idx].isNull = (val == null)); // Sets whether the value is or not null. - paramDefined[index] = true; - } - - // juliana@223_3: PreparedStatement.setNull() now works for blobs. - /** - * Sets null in a given field. - * - * @param index The index of the parameter. - * @throws SQLParseException If the index is for the where clause. - */ - void setNull(int index) throws SQLParseException - { - checkIndex(index); // Checks if the index is within the range. - - if (index < paramCount) // The parameter is in the update clause. - { - int idx = paramIndexes[index] & 0xFF; - SQLValue value = record[idx]; - - // Sets the null value in its list. - value.asString = null; - value.asBlob = null; - Utils.setBit(storeNulls, idx, value.isNull = paramDefined[index] = true); - } - else // The parameter is in the where clause. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PARAM_NULL)); - } - - /** - * Clear all parameter values of a prepared statement update. - */ - void clearParamValues() - { - int i = paramCount, - j; - SQLValue value; - SQLValue[] recordAux = record; - SQLBooleanClause clause = whereClause; - - // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - short[] paramIndexesAux = paramIndexes; - - byte[] storeNullsAux = storeNulls; - - Convert.fill(paramDefined, 0, paramDefined.length, false); // Cleans the parameter values of the update clause. - - while (--i >= 0) - { - (value = recordAux[j = paramIndexesAux[i] & 0xFF]).asString = null; - Utils.setBit(storeNullsAux, j, value.isNull = false); - value.asBlob = null; - } - - if (clause != null) // Cleans the parameter values of the where clause. - { - SQLBooleanClauseTree[] paramList = clause.paramList; - i = clause.paramCount; - while (--i >= 0) - paramList[i].isParamValueDefined = false; - } - } - - /** - * Checks if all parameters values are defined. - * - * @throws DriverException If not all parameters values are defined. - */ - void allParamValuesDefined() - { - int i = paramCount; - boolean[] paramDefinedAux = paramDefined; - SQLBooleanClause clause = whereClause; - - // Checks if all the parameters of the update clause are defined. - while (--i >= 0) - if (!paramDefinedAux[i]) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_NOT_ALL_PARAMETERS_DEFINED)); - - if (clause != null) - { - SQLBooleanClauseTree[] paramList = clause.paramList; - - // Checks if all pararameters of the where clause are defined. - i = clause.paramCount; - while (--i >= 0) - if (!paramList[i].isParamValueDefined) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_NOT_ALL_PARAMETERS_DEFINED)); - } - } - - /** - * Executes an update statement. - * - * @param driver A connection with Litebase. - * @return The number of rows updated. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - int litebaseDoUpdate(LitebaseConnection driver) throws IOException, InvalidDateException, InvalidNumberException - { - Table table = rsTable.table; - SQLBooleanClause where = whereClause; - - if (table.db.db == null) // juliana@201_28: If a table is re-created after the prepared statement is parsed, there won't be a NPE. - table = rsTable.table = driver.getTable(rsTable.tableName); - - int records = 0; - - table.verifyNullValues(record, storeNulls, SQLElement.CMD_UPDATE); - ResultSet rs = table.createSimpleResultSet(where); // Creates the result set that will be used to update the rows. - - if (where != null) // Verifies if there are any parameters missing. - where.sqlBooleanClausePreVerify(); - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since - // its last opening. - table.setModified(); // Sets the table as not closed properly. - - while (rs.getNextRecord()) - { - table.writeRecord(record, rs.pos); - records++; - } - - return records; - } - - // nowosad@200 - /** - * Binds a SQL UPDATE expression. - * - * @param updateStmt The update statement to be binded. - * @return The update statement binded. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - SQLUpdateStatement litebaseBindUpdateStatement(LitebaseConnection driver) throws IOException, InvalidDateException, InvalidNumberException - { - Table table = rsTable.table = driver.getTable(rsTable.tableName); // Gets the statement table. - int valuesCount = record.length; - - // juliana@262_1: now it is not allowed duplicated fields in an update statement. - if (valuesCount >= table.columnCount) - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATED_COLUMN_NAME)); - - paramCount = 0; - int i = -1; - while (++i < valuesCount) // Checks if there are undefined values. - { - String string; - if ((string = record[i].asString) != null && string.equals("?")) // Identifies the values that are placeholders for parameters. - paramIndexes[paramCount++] = (short)i; - } - - table.reorder(this); // Makes sure the fields are in correct order, aligned with the table order. - table.convertStringsToValues(record); // Converts the values to be updated into its correct type. - - if (whereClause != null) // Binds the where clause to its table. - whereClause.bindColumnsSQLBooleanClause(table.htName2index, table.columnTypes, rsTable); - - return this; - } - - // juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. - /** - * Checks the prepared statement parameter index. - * - * @param index The index of the prepared statement parameter. - * @throws IllegalArgumentException If the index is out of range. - */ - void checkIndex(int index) throws IllegalArgumentException - { - if (index < 0 || index >= (paramCount + (whereClause == null? 0 : whereClause.paramCount))) // Checks if the index is within the range. - throw new IllegalArgumentException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_PARAMETER_INDEX)); - } -} diff --git a/LitebaseSDK/src/java/litebase/SQLValue.java b/LitebaseSDK/src/java/litebase/SQLValue.java deleted file mode 100644 index 8cf3a006dc..0000000000 --- a/LitebaseSDK/src/java/litebase/SQLValue.java +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.IOException; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Represents a value which can be inserted in a column of a table. - */ -@Deprecated -class SQLValue -{ - /** - * Represents the SHORT data type. - */ - int asShort; - - /** - * Represents the INT data type. - */ - int asInt; - - /** - * Represents the LONG data type. - */ - long asLong; - - /** - * Represents the DOUBLE and FLOAT data type. - */ - double asDouble; - - /** - * Represents the CHARS, VARCHAR, CHARS NOCASE, and VARCHAR NOCASE data types. - */ - String asString; - - /** - * Represents the BLOB data type. - */ - byte[] asBlob; - - /** - * Indicates if the record is null or not. - */ - boolean isNull; - - /** - * Creates an array of SQLValues. - * - * @param count The array size. - * @return The SQLValue array. - */ - static SQLValue[] newSQLValues(int count) - { - SQLValue[] a = new SQLValue[count]; - while (--count >= 0) - a[count] = new SQLValue(); - return a; - } - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - /** - * Clones a SQLValue for index usage. - * - * @param The cloned SQLValue. - */ - void cloneSQLValue(SQLValue sqlValue) - { - sqlValue.asDouble = asDouble; - sqlValue.asInt = asInt; - sqlValue.asLong = asLong; - sqlValue.asShort = asShort; - sqlValue.asString = asString; - sqlValue.isNull = false; - } - - // rnovais@568_10 - // You must to handle the null values before. If the value is null, this method can't be called. - /** - * Applies the function on the value. - * - * @param sqlFunction The code of the function to be applied. - * @param paramDataType The data type of the parameter. - */ - void applyDataTypeFunction(int sqlFunction, int paramDataType) - { - switch (sqlFunction) - { - case SQLElement.FUNCTION_DT_YEAR: - asShort = asInt / 10000; - break; - case SQLElement.FUNCTION_DT_MONTH: - asShort = asInt / 100 % 100; - break; - case SQLElement.FUNCTION_DT_DAY: - asShort = asInt % 100; - break; - case SQLElement.FUNCTION_DT_HOUR: - asShort = asShort / 10000000; - break; - case SQLElement.FUNCTION_DT_MINUTE: - asShort = asShort / 100000 % 100; - break; - case SQLElement.FUNCTION_DT_SECOND: - asShort = asShort / 1000 % 100; - break; - case SQLElement.FUNCTION_DT_MILLIS: - asShort %= 1000; - break; - case SQLElement.FUNCTION_DT_ABS: - switch (paramDataType) - { - case SQLElement.SHORT: - asShort = (asShort < 0)? -asShort : asShort; - break; - case SQLElement.INT: - asInt = (asInt < 0)? -asInt : asInt; - break; - case SQLElement.LONG: - asLong = (asLong < 0)? -asLong : asLong; - break; - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - asDouble = (asDouble <= 0.0D)? - asDouble : asDouble; - } - break; - case SQLElement.FUNCTION_DT_UPPER: - asString = asString.toUpperCase(); - break; - case SQLElement.FUNCTION_DT_LOWER: - asString = asString.toLowerCase(); - } - } - - // juliana@253_5: removed .idr files from all indices and changed its format. - /** - * Compares 2 values. - * - * @param value The value to be compared against. - * @param type The types of the values being compared. - * @param isNull1 Indicates if the value being compared is null. - * @param isNull2 Indicates if the value being compared against is null. - * @param plainDB the plainDB of a table if it is necessary to load a string. - * @return 0 if the values are identical; a positive number if the value being compared is greater than the one being compared against; otherwise, - * a negative number. - * @throws IOException If an internal method throws it. - */ - int valueCompareTo(SQLValue value, int type, boolean isNull1, boolean isNull2, PlainDB plainDB) throws IOException - { - if (isNull1 || isNull2) // A null value is always considered to be the greatest value. - return (isNull1 == isNull2)? 0 : (isNull1? 1 : -1); - - switch (type) - { - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - if (asString == null) - return 0; - if (value.asString == null) - { - plainDB.dbo.setPos(value.asInt); - value.asString = plainDB.loadString(); - } - if (type == SQLElement.CHARS_NOCASE) - return asString.toLowerCase().compareTo(value.asString.toLowerCase()); - return asString.compareTo(value.asString); - - case SQLElement.SHORT: - return asShort - value.asShort; - - case SQLElement.DATE: // rnovais@567_2 - case SQLElement.INT: - return asInt - value.asInt; - - case SQLElement.LONG: - long vl = asLong - value.asLong; - return (vl == 0)? 0 : (vl > 0)? 1 : -1; - - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - double vd = asDouble - value.asDouble; - return (vd == 0)? 0 : (vd > 0)? 1 : -1; - - case SQLElement.DATETIME: // rnovais@_567_2 @_570_10 - int i = asInt - value.asInt; - if (i == 0) - i = asShort - value.asShort; - return i; - } - return 0; - } - - // rnovais@567_2 - /** - * Validates a string value as a date or datetime according to the value type. - * - * @param tempDate A temporary Date Object reused to save memory. - * @param valueType The type of the value being validate. - * @throws InvalidDateException If it is thrown by an internal method. - */ - void validateDateTime(Date tempDate, int valueType) throws SQLParseException, InvalidDateException - { - if (asString.indexOf('%') == -1) - if (valueType == SQLElement.DATE) - asInt = tempDate.set(asString, Settings.DATE_YMD); - else - parseDateTime(tempDate, asString); - } - - // juliana@202_2: corrected a bug that might let Litebase issue an InvalidDateException if a date was inserted in a datetime field. - /** - * Parses a datetime string into the Litebase format. - * - * @param tempDate A temporary Date Object reused to save memory. - * @param strVal The string to be parsed. - * @throws SQLParseException If an the string is not a valid date time. - * @throws InvalidDateException If an internal method throws it. - */ - void parseDateTime(Date tempDate, String strVal) throws SQLParseException, InvalidDateException - { - strVal = strVal.trim(); - int pos = strVal.lastIndexOf(' '); // By using lastIndexOf another trim for the time is not necessary. - if (pos == -1) // only date? - { - asInt = tempDate.set(strVal, Settings.DATE_YMD); - asShort = 0; // If it has only a date, the time part must be 0. - } - else - { - asInt = tempDate.set(strVal.substring(0, pos), Settings.DATE_YMD); - asShort = Utils.testAndPrepareTime(strVal.substring(pos + 1)); // Gets the time part, skipping the space. - } - } -} diff --git a/LitebaseSDK/src/java/litebase/Table.java b/LitebaseSDK/src/java/litebase/Table.java deleted file mode 100644 index 1dac6419e2..0000000000 --- a/LitebaseSDK/src/java/litebase/Table.java +++ /dev/null @@ -1,3132 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.util.*; - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * The structure of a table. - */ -@Deprecated -class Table -{ - /** - * Current table format version. - */ - static final int VERSION = 203; // juliana@230_12 - - // ############ JOIN OPERATION CONSTANTS ########### - /** - * Indicates the end of the table in join operations. - */ - private static final int NO_RECORD = 0; - - /** - * Indicates that the row can be used in join operations. - */ - private static final int VALIDATION_RECORD_OK = 1; - - /** - * Indicates that the row can't be used in join operations. - */ - private static final int VALIDATION_RECORD_NOT_OK = 2; - - /** - * Indicates that the row validation is incomplete and must be continued in join operations. - */ - private static final int VALIDATION_RECORD_INCOMPLETE = 3; - - /** - * Used internally on booleanTreeEvaluateJoin(). The current branch was validated as true. - */ - private static final int VALIDATION_RECORD_INCOMPLETE_OK = 4; - - /** - * Indicates that a column has index. Used in writeRecord(). - */ - private static final int HAS_IDX = 1; - - /** - * Indicates that a column has null. Used in writeRecord(). - */ - private static final int HAS_NULLVAL = 2; - - /** - * Indicates that a column old value had null. Used in writeRecord(). - */ - private static final int ISNULL_VOLDS = 4; - - /** - * Indicates if a table was saved correctly the last time it was modified. - */ - static final int IS_SAVED_CORRECTLY = 1; - - /** - * Indicates if the table strings are stored in the ascii or unicode format. - */ - static final int IS_ASCII = 2; - - /** - * Indicates if the table uses cryptography. - */ - static final int USE_CRYPTO = 3; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * The counter of the current rowid. The rowid is continuously incremented so that two elements will never have the same - * one, even if elements are deleted.

    The record attributes are stored in the first two bits of the rowid. - */ - int currentRowId = 1; - - /** - * The attributes of the row. - */ - int auxRowId = Utils.ATTR_DEFAULT_AUX_ROWID; // rnovais@570_61 - - /** - * The number of columns of this table. - */ - int columnCount; - - /** - * The number of deleted rows. - */ - int deletedRowsCount; - - /** - * The primary key column. - */ - int primaryKeyCol = Utils.NO_PRIMARY_KEY; - - /** - * The index of the composed primary key. - */ - int composedPK = Utils.NO_PRIMARY_KEY; - - /** - * Number of composed indices. - */ - int numberComposedIndices; - - /** - * Number of composed primary key columns. - */ - int numberComposedPKCols; - - /** - * Used to order the tables. - */ - int weight; - - /** - * Used to return the number of rows that a select without a where clause returned. - */ - int answerCount = -1; // juliana@230_14 - - /** - * The current table version. - */ - int version; // juliana@230_12 - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - /** - * Indicates that a table has been modified and must be marked as not closed properly after opened and before closed. - */ - boolean isModified; - - // juliana@270_27: now purge will also really purge the table if it only suffers updates. - /** - * Indicates if the table was updated after the last time it was opened. - */ - boolean wasUpdated; - - /** - * The full name of the table. - */ - String name; - - /** - * The corresponding files of the table. - */ - PlainDB db; - - /** - * Given a column name, returns its index for this table. rowid, a special column, is always column 0. - */ - IntHashtable htName2index; - - /** - * Just for the case when the column has a default value but the user explicited the insert or update of a null. - */ - byte[] storeNulls; - - /** - * The column attributes. - */ - byte[] columnAttrs; - - /** - * The composed primary key columns. - */ - byte[] composedPrimaryKeyCols; - - /** - * Contains the null values. - */ - byte[][] columnNulls = new byte[3][]; - - /** - * The hashes of the column names. - */ - int[] columnHashes; - - /** - * Column offsets within the record. - */ - short[] columnOffsets; - - /** - * Column types (SHORT, INT, LONG, FLOAT, DOUBLE, CHARS, - * CHARS_NOCASE) - */ - byte[] columnTypes; - - /** - * Column sizes (only used for CHAR and BLOB types). - */ - int[] columnSizes; - - /** - * The column names. If null, the column names are not available because it is a temporary table. - */ - String[] columnNames; // nowosad@_200 - - /** - * Contains the default values for the columns. - */ - SQLValue[] defaultValues; - - /** - * Existing column indices for each column, or null if the column has no index. - */ - Index[] columnIndices; - - /** - * Existing composed column indices for each column, or null if the table has no composed index. - */ - ComposedIndex[] composedIndices = new ComposedIndex[SQLBooleanClause.MAX_NUM_INDEXES_APPLIED]; - - /** - * A buffer to store the table meta data. - */ - private ByteArrayStream tsmdBas; - - /** - * A data stream for the table meta data. - */ - private DataStreamLB tsmdDs; // juliana@253_8: now Litebase supports weak cryptography. - - /** - * Stores old values read from the table. This is used by writeRecord() in order to reduce memory allocation. - */ - SQLValue[] gvOlds; - - /** - * Stores flags from the record. This is used by writeRecord() in order to reduce memory allocation. - */ - byte[] ghas; - - /** - * An array to store the primary key values. Used in writeRecord(). - */ - private SQLValue[] primaryKeyValues; - - /** - * An array to store the primary key old values when doing an update. Used in writeRecord(). - */ - private SQLValue[] primaryKeyOldValues; - - /** - * A map with rows that satisfy totally the query WHERE clause. - */ - byte[] allRowsBitmap; // juliana@230_14 - - /** - * An array of only one integer to convert to a byte array. - */ - private int[] oneInt = new int[1]; - - /** - * Verifies if the index already exists. - * - * @param columnNumbers The columns that are part of this index. - * @return 0 for simple indices. For composed index, if there was this same index, it returns the negative number of the this old one; otherwise, - * it returns the new number. - * @throws AlreadyCreatedException If an index already exists. - */ - int verifyIfIndexAlreadyExists(byte[] columnNumbers) throws AlreadyCreatedException - { - int indexCount = columnNumbers.length, - idx = -1; - - if (indexCount == 1) // Simple index. - { - if (columnIndices[idx = columnNumbers[0]] != null) - throw new AlreadyCreatedException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INDEX_ALREADY_CREATED) + columnNames[idx]); - return 0; - } - else // Composed index. - { - ComposedIndex currCompIndex; - ComposedIndex[] compIndices = composedIndices; - byte[] columns; - boolean alreadyExists; - int i = numberComposedIndices, - j; - - if (i == 0) - return 1; // First index number. - while (--i >= 0) - { - currCompIndex = compIndices[i]; - alreadyExists = true; - - columns = currCompIndex.columns; - j = columns.length; - - if (j == indexCount) // juliana@253_2: corrected a bug if a composed index with less columns were created after one with more columns. - { - while (--j >= 0) - if (columnNumbers[j] != columns[j]) - { - alreadyExists = false; - break; - } - } - else - alreadyExists = false; - - if (alreadyExists) - { - StringBuffer cols = db.driver.sBuffer; - String[] colNames = columnNames; - - // Builds the exception message. - cols.setLength(0); - cols.append(colNames[currCompIndex.columns[0]]); - j = columns.length; - i = 0; - while (++i < j) - cols.append(", ").append(colNames[columns[i]]); - throw new AlreadyCreatedException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INDEX_ALREADY_CREATED) + cols); - } - } - return numberComposedIndices + 1; - } - } - - /** - * Drops an index. - * - * @param column The column of the index dropped. - * @throws IOException If an internal method throws it. - * @throws DriverException If the column does not have an index. - */ - void driverDropIndex(int column) throws IOException - { - Index index = columnIndices[column]; - - if (index == null) // Column does not have an index. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_DOESNOT_HAVE_AN_INDEX) + columnNames[column]); - - // Deletes the index of this table. - index.fnodes.f.delete(); - columnIndices[column] = null; - - // juliana@227_6 - columnAttrs[column] &= ~Utils.ATTR_COLUMN_HAS_INDEX; // Deletes the INDEX bit from the attributes. - - // Saves the meta data. - tableSaveMetaData(Utils.TSMD_EVERYTHING); // guich@560_24 - } - - /** - * Drops a composed index. - * - * @param columns The columns of the composed index. - * @param indexId The id of the composed index or -1 if its position is not known. - * @param saveMD Indicates if the meta data is to be saved. - * @throws IOException If an internal method throws it. - * @throws DriverException If the table does not have the desired composed index to be dropped. - */ - void driverDropComposedIndex(byte[] columns, int indexId, boolean saveMD) throws DriverException, IOException - { - ComposedIndex ci = null; - int indexCount = columns.length; - ComposedIndex[] compIndices = composedIndices; - boolean found = true; - int i = numberComposedIndices, - j; - if (indexId >= 0) - ci = composedIndices[i = indexId]; - else - while (--i >= 0) - { - found = true; - ci = compIndices[i]; - - if (ci.columns.length == indexCount) - { - j = indexCount; - while (--j >= 0) - if (columns[j] != ci.columns[j]) - { - found = false; - break; - } - if (found) - break; - } - else - found = false; - } - - if (found && ci != null) // Removes the index. - { - ci.index.fnodes.f.delete(); - ci.index = null; - - // juliana@201_16: When a composed index is deleted, its information is now deleted from the metadata. - (compIndices[i] = compIndices[--numberComposedIndices]).indexId = i + 1; - } - else // The given columns do not have a composed index. - { - StringBuffer cols = db.driver.sBuffer; - String[] colNames = columnNames; - - cols.setLength(0); - cols.append(colNames[columns[0]]); - j = 0; - while (++j < indexCount) - cols.append(", ").append(colNames[columns[j]]); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_DOESNOT_HAVE_AN_INDEX) + cols); - } - if (saveMD) - tableSaveMetaData(Utils.TSMD_EVERYTHING); - } - - // juliana@227_6: drop index * on table_name wold make the index reapear after closing the driver and reusing table_name. - /** - * Deletes all indices of a table. - * - * @return The number of indices deleted. - * @throws IOException If an internal method throws it. - */ - int deleteAllIndices() throws IOException - { - Index[] indices = columnIndices; - ComposedIndex[] compIndices = composedIndices; - byte[] attrs = columnAttrs; - int i = indices.length, - count = 0, - pk = primaryKeyCol, - cpk = composedPK; - - // Unique index. - while (--i >= 0) - if (i != pk && indices[i] != null) - { - indices[i].fnodes.f.delete(); - indices[i] = null; - attrs[i] &= ~Utils.ATTR_COLUMN_HAS_INDEX; - count++; - } - - // juliana@201_33: When all indices are dropped by the user, the composed primary key can't be deleted. - // Composed index. - i = numberComposedIndices; - while (--i >= 0) - if (i != cpk) - { - driverDropComposedIndex(compIndices[i].columns, i, false); - count++; - } - - if (count > 0) - tableSaveMetaData(Utils.TSMD_EVERYTHING); // guich@560_24 - return count; - } - - /** - * Computes the column offsets of the table columns. - * - * @throws IOException If the table has duplicated columns. - */ - private void computeColumnOffsets() throws IOException - { - int sum = 0, - i = -1, - n = columnCount; - boolean recomputing = columnOffsets != null; - byte[] types = columnTypes; - PlainDB plainDB = db; - - if (!recomputing) // Does not create the array 2 times. - columnOffsets = new short[n + 1]; - - short[] offsets = columnOffsets; - - while (++i < n) - { - offsets[i] = (short)sum; // Total offset till now. - sum += Utils.typeSizes[types[i]]; // Gets the size of this column. - } - offsets[i] = (short)sum; // The offset for the last column. - - // the number of bytes necessary to store the columns. Each column in a table correspond to one bit. - // Added a number of bytes corresponding to the null values and to the crc code. - sum += ((n + 7) >> 3) + 4; // juliana@220_4 - - plainDB.setRowSize(sum, plainDB.basbuf == null || sum > plainDB.basbuf.length? new byte[sum] : plainDB.basbuf); // Sets the new row size. - - if (!recomputing) - { - IntHashtable hashTable = htName2index = new IntHashtable(n); - int[] hashes = columnHashes; - - - if (name == null) // The hashes of the columns must be put in a hash table. - while (--n >= 0) - hashTable.put(hashes[n], n); - else - while (--n >= 0) - try // There can't be duplicated hashes. - { - hashTable.get(hashes[n]); - plainDB.remove(); - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_DUPLICATED_COLUMN_NAME)); - } - catch (ElementNotFoundException e) - { - hashTable.put(hashes[n], n); - } - } - } - - // rnovais@570_75 juliana@220_2 - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Loads the meta data of a table. - * - * @param appCrid The application id, used to identify the tables application. - * @param sourcePath The path where the table is stored. - * @param throwException Indicates that a TableNotClosedException should be thrown. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws DriverException If the table is in an incompatible format. - * @throws TableNotClosedException If the table was not properly close when opened last time. - */ - private void tableLoadMetaData(String appCrid, String sourcePath, boolean throwException) throws IOException, InvalidDateException, - DriverException, TableNotClosedException - { - PlainDB plainDB = db; - NormalFile dbFile = (NormalFile)plainDB.db; - byte[] bytes = plainDB.readMetaData(); // Reads the meta data. - boolean exist; - String nameAux; - int flags; - File idxFile; - - DataStreamLB ds = new DataStreamLB(new ByteArrayStream(bytes), plainDB.useCrypto); - - // The currentRowId is found from the last non-empty record, not from the metadata. - if (!(((bytes[0] & USE_CRYPTO) == 0) ^ plainDB.useCrypto) - && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0 && (bytes[0] == 0 || bytes[0] == 1 || bytes[0] == 3)) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - dbFile.close(); - plainDB.dbo.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_WRONG_CRYPTO_FORMAT)); - } - - if (bytes[0] == 1) - plainDB.useOldCrypto = true; - - ds.skipBytes(4); // It is not necessary to read the last position of the blobs and strings file. - plainDB.headerSize = ds.readShort(); // Reads the header size. - - if (plainDB.headerSize == 0) // The header size can't be zero. - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - dbFile.close(); - plainDB.dbo.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_CORRUPTED) + name + '!'); - } - - // If the header needs to be bigger, re-creates the metadata buffer with the correct size and skips the bytes already read. - if (plainDB.headerSize != bytes.length) - (ds = new DataStreamLB(new ByteArrayStream(bytes = plainDB.readMetaData()), plainDB.useCrypto)).skipBytes(6); - - plainDB.dbo.finalPos = plainDB.dbo.size; // This does not let the user lose some database objects. - - // Checks if the table strings has the same format of the connection. - if ((((flags = ds.readByte()) & IS_ASCII) != 0 && !plainDB.isAscii) || ((flags & IS_ASCII) == 0) && plainDB.isAscii) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - dbFile.close(); - plainDB.dbo.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_WRONG_STRING_FORMAT)); - } - - // juliana@251_11: removed a possible exception when recovering an ascii table with indices on JavaSE and Blackberry. - // juliana@220_2: added TableNotCreatedException which will be raised whenever a table is not closed properly. - // If the table was not correctly closed, throws an specific exception to the user. - if ((flags &= IS_SAVED_CORRECTLY) == 0 && throwException) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - dbFile.close(); - plainDB.dbo.close(); - throw new TableNotClosedException(name.substring(5)); - } - - // juliana@230_12: improved recover table to take .dbo data into consideration. - if ((version = ds.readShort()) < VERSION - 1) // The tables version must be the same as Litebase version. - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - dbFile.close(); - plainDB.dbo.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_WRONG_VERSION) + (" (" + version) + ')'); - } - - deletedRowsCount = ds.readInt(); // Deleted rows count. - auxRowId = ds.readInt(); // rnovais@570_61: reads the auxiliary rowid. - - // juliana@230_5: Corrected a AIOBE when using a table created on Windows 32, Windows CE, Linux, Palm, Android, iPhone, or iPad using - // primary key on BlackBerry and Eclipse. - primaryKeyCol = ds.readByte(); // juliana@114_9: the simple primary key column. - ds.skipBytes(1); - composedPK = ds.readByte(); // The composed primary key index. - ds.skipBytes(1); - columnCount = ds.readUnsignedShort(); // Reads the column count. - - int n = columnCount, - i = -1, - j; - - if (n <= 0) // The column count can't be negative. - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - dbFile.close(); - plainDB.dbo.close(); - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_CORRUPTED) + name + '!'); - } - byte[] attrs = columnAttrs = new byte[n]; - int[] hashes = columnHashes = new int[n]; - byte[] types = columnTypes = new byte[n]; - int[] sizes = columnSizes = new int[n]; - SQLValue[] values = defaultValues = new SQLValue[n]; - - columnIndices = new Index[n]; - storeNulls = new byte[(n + 7) >> 3]; - - // The number of bytes necessary to store the columns. Each column in a table corresponds to one bit. - // juliana@201_21: The null columns information must be created before openning the indices when reading the table meta data. - j = (n + 7) >> 3; // Caution: n will be reused below. - columnNulls[0] = new byte[j]; - columnNulls[1] = new byte[j]; - - ds.readBytes(attrs, 0, n); // Reads the column attributes. - ds.readBytes(types, 0, n); // Reads the column types. - - i = -1; - while (++i < n) // Reads the column sizes. - sizes[i] = ds.readInt(); - - // juliana@224_1: corrected an inconsistency between java and native versions that would raise an IndexOutOfBoundsException when - // using a table created on Windows 32, Windows CE, Palm, Android, Linux, or iPhone on Java or BlackBerry. - ds.skipBytes(2); - String[] names = columnNames = ds.readStringArray(n); // Reads the column names. - - i = -1; - while (++i < n) // Computes the hashes. - hashes[i] = names[i].hashCode(); - - computeColumnOffsets(); // Computes the column offsets. - - String fullName, - tableName = name; - - gvOlds = new SQLValue[n]; // Stores old values read from the table. - ghas = new byte[n]; // Stores flags from the record. - - // Creates the primary key records. - primaryKeyValues = new SQLValue[n]; - primaryKeyOldValues = SQLValue.newSQLValues(n); - - i = n; - nameAux = tableName + '$'; - while (--i >= 0) // Loads the indices. - if ((attrs[i] & Utils.ATTR_COLUMN_HAS_INDEX) != 0) - { - // juliana@227_21: corrected a bug of recover table not working correctly if the table has indices. - if ((exist = new File(fullName = Utils.getFullFileName(nameAux + i + ".idk", sourcePath)).exists()) && flags == 0) - { - idxFile = new File(fullName, File.READ_WRITE); - idxFile.setSize(0); - exist = false; - idxFile.close(); - } - - indexCreateIndex(tableName, i, new int[]{sizes[i]}, new byte[]{types[i]}, appCrid, sourcePath, exist); - if (!exist && flags != 0) // One of the files doesn't exist. juliana@227_21 - { - // juliana@230_8: corrected a possible index corruption if its files are deleted and the application crashes after recreating it. - setModified(); // Sets the table as not closed properly. - tableReIndex(i, null, false); - } - } - - // Now the current rowid can be fetched. - dbFile.setPos(plainDB.headerSize + (plainDB.rowCount > 0 ? plainDB.rowCount - 1 : 0) * plainDB.rowSize); - currentRowId = (auxRowId != Utils.ATTR_DEFAULT_AUX_ROWID? auxRowId - : ((new DataStreamLB(dbFile, plainDB.useCrypto).readInt() & Utils.ROW_ID_MASK) + 1)) & Utils.ROW_ID_MASK; - - i = 0; - while (++i < n) // Reads the default values. - if ((attrs[i] & Utils.ATTR_COLUMN_HAS_DEFAULT) != 0) // Tests if it has default values. - { - values[i] = new SQLValue(); - - switch (types[i]) - { - case SQLElement.CHARS: - case SQLElement.CHARS_NOCASE: - values[i].asString = new String(ds.readChars()); - break; - - case SQLElement.SHORT: - values[i].asShort = ds.readShort(); - break; - - case SQLElement.DATE: // stored as int. - case SQLElement.INT: - values[i].asInt = ds.readInt(); - break; - - case SQLElement.LONG: - values[i].asLong = ds.readLong(); - break; - - case SQLElement.FLOAT: - values[i].asDouble = ds.readFloat(); - break; - - case SQLElement.DOUBLE: - values[i].asDouble = ds.readDouble(); - break; - - case SQLElement.DATETIME: - values[i].asInt = ds.readInt(); // date - values[i].asShort = ds.readInt(); // time - } - } - - int numCompIndices = numberComposedIndices = ds.readByte(); // Reads the composed indices. - - if (numCompIndices > 0) - { - int column, - aComposedPK = composedPK, - indexId, - numColumns; - byte[] columns; - int[] columnSizes; - byte[] columnTypes; - ComposedIndex[] compIndices = composedIndices; - - i = -1; - nameAux = tableName + '&'; - while (++i < numCompIndices) - { - indexId = ds.readByte(); // The composed index id. - numColumns = ds.readByte(); // Number of columns on the composed index. - ds.skipBytes(1); - columns = new byte[numColumns]; - columnSizes = new int[numColumns]; - columnTypes = new byte[numColumns]; - - j = -1; - while (++j < numColumns) - { - column = columns[j] = ds.readByte(); // Columns of this composed index. - columnSizes[j] = sizes[column]; - columnTypes[j] = types[column]; - } - - // juliana@227_21: corrected a bug of recover table not working correctly if the table has indices. - if ((exist = new File(fullName = Utils.getFullFileName(nameAux + indexId + ".idk", sourcePath)).exists()) && flags == 0) - { - idxFile = new File(fullName, File.READ_WRITE); - idxFile.setSize(0); - exist = false; - idxFile.close(); - } - - indexCreateComposedIndex(tableName, columns, columnSizes, columnTypes, indexId, aComposedPK == i, appCrid, false, sourcePath, exist); - if (!exist && flags != 0) // One of the files doesn't exist. - { - // juliana@230_8: corrected a possible index corruption if its files are deleted and the application crashes after recreating it. - setModified(); // Sets the table as not closed properly. - tableReIndex(indexId - 1, compIndices[indexId - 1], false); // juliana@227_21 - } - - } - } - - // Reads the composed primary key. - if ((n = numberComposedPKCols = ds.readByte()) > 0) - { - byte[] compPrimaryKeyCols = composedPrimaryKeyCols = new byte[n]; - i = -1; - while (++i < n) - compPrimaryKeyCols[i] = ds.readByte(); // The composed primary key cols. - } - } - - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Saves the table meta data - * - * @param saveType The kind of save. It can be one out of TSMD_ONLY_DELETEDROWSCOUNT, - * TSMD_ONLY_PRIMARYKEYCOL, TSMD_EVERYTHING, or TSMD_ONLY_AUXROWID. - * @throws IOException If an internal method throws it. - */ - void tableSaveMetaData(int saveType) throws IOException - { - // Stores the changeable information. - int n = columnCount, - i = -1, - j = isModified? 0 : Table.IS_SAVED_CORRECTLY, - numberColumns; - byte[] types = columnTypes; - int[] sizes = columnSizes; - byte[] attrs = columnAttrs; - byte[] columns; - SQLValue[] values = defaultValues; - ComposedIndex[] compIndices = composedIndices; - ComposedIndex ci; - ByteArrayStream auxBas = tsmdBas; - DataStreamLB auxDs = tsmdDs; - PlainDB plainDB = db; - - if (auxBas != null) // If the buffer is not empty, only resets it. - auxBas.reset(); - else // Otherwise, allocates it. - auxDs = tsmdDs = new DataStreamLB(auxBas = tsmdBas = new ByteArrayStream(plainDB.headerSize), plainDB.useCrypto); - - auxBas.getBuffer()[0] = (byte)(plainDB.useCrypto? (plainDB.useOldCrypto? 1 : USE_CRYPTO) : 0); - auxDs.skipBytes(4); // The strings and blobs final position is deprecated. - auxDs.writeShort(plainDB.headerSize); // Saves the header size. - auxDs.writeByte(plainDB.isAscii? IS_ASCII | j : j); // juliana@226_4: table is not saved correctly if modified. - auxDs.writeShort(version); // The table format version. - auxDs.writeInt(deletedRowsCount); // Saves the deleted rows count. - - if (saveType != Utils.TSMD_ONLY_DELETEDROWSCOUNT) // More things other than the deleted rows count must be saved. - { - auxDs.writeInt(auxRowId); // rnovais@570_61: saves the auxiliary rowid. - - if (saveType != Utils.TSMD_ONLY_AUXROWID) // More things other than the auxiliary row id must be saved. - { - // juliana@230_5: Corrected a AIOBE when using a table created on Windows 32, Windows CE, Linux, Palm, Android, iPhone, or iPad using - // primary key on BlackBerry and Eclipse. - auxDs.writeByte(primaryKeyCol); // Saves the primary key col. - auxDs.writeByte(0); - auxDs.writeByte(composedPK); // juliana@114_9: saves the composed primary key index. - auxDs.writeByte(0); - - if (saveType != Utils.TSMD_ONLY_PRIMARYKEYCOL) // More things other than the primary key col must be saved. - { - auxDs.writeShort(n); // Saves the number of columns. - auxDs.writeBytes(attrs, 0, n); // Saves the column attributes. - - if (saveType == Utils.TSMD_EVERYTHING) // Stores the rest. - { - auxDs.writeBytes(types, 0, n); // Stores the column types. - - i = -1; - while (++i < n) // Stores the column sizes. - auxDs.writeInt(sizes[i]); - - auxDs.writeStringArray(columnNames); // Stores the column names. - - i = 0; - while (++i < n) // Saves the default values. - if (values[i] != null) - switch (types[i]) - { - case SQLElement.CHARS_NOCASE: - case SQLElement.CHARS: - String s = values[i].asString; - auxDs.writeChars(s, Math.min(s.length(), sizes[i])); - break; - - case SQLElement.SHORT: - auxDs.writeShort(values[i].asShort); - break; - - case SQLElement.DATE: - case SQLElement.INT: - auxDs.writeInt(values[i].asInt); - break; - - case SQLElement.LONG: - auxDs.writeLong(values[i].asLong); - break; - case SQLElement.FLOAT: - auxDs.writeFloat(values[i].asDouble); - break; - - case SQLElement.DOUBLE: - auxDs.writeDouble(values[i].asDouble); - break; - - case SQLElement.DATETIME: - auxDs.writeInt(values[i].asInt); - auxDs.writeInt(values[i].asShort); - } - - auxDs.writeByte(n = numberComposedIndices); // Number of composed indices. - - i = -1; - while (++i < n) // Stores the composed indices. - { - auxDs.writeByte((ci = compIndices[i]).indexId); // The composed index id. - auxDs.writeByte(numberColumns = ci.columns.length); // Number of columns on the composed index. - auxDs.writeByte(0); // Ignored. - columns = ci.columns; - j = -1; - while (++j < numberColumns) - auxDs.writeByte(columns[j]); // Columns of this composed index. - } - - // Number of columns on composed primary key. If 0, there's no composed primary key. - auxDs.writeByte(n = numberComposedPKCols); - columns = composedPrimaryKeyCols; - i = -1; - while (++i < n) // Stores the composed primary key. - auxDs.writeByte(columns[i]); // The column of the composed primary key. - - } - } - } - } - - plainDB.writeMetaData(auxBas.getBuffer(), auxBas.getPos()); - ((NormalFile)(plainDB.db)).flushCache(); // juliana@223_11: table meta data is now always flushed imediately after being changed. - } - - /** - * Reorder the values of a statement to match the table definition. - * - * @param stmt An insert or an update statement. - * @throws SQLParseException If a field in the field list does not exist. - * @throws IOException If an internal method throws it. - */ - void reorder(SQLStatement stmt) throws SQLParseException, IOException - { - byte[] nulls; - byte[] tableNulls = storeNulls; - String[] fields; - SQLValue[] record; - short[] paramIndexes; // juliana@253_14: corrected a possible AIOBE if the number of parameters of a prepared statement were greater than 128. - SQLInsertStatement insertStmt = null; - SQLUpdateStatement updateStmt = null; - int idx, - i = -1, - length, - numParams = 0; - boolean isInsert = stmt.type == SQLElement.CMD_INSERT; - - if (isInsert) // Insert statement. - { - nulls = (insertStmt = (SQLInsertStatement)stmt).storeNulls; // Cleans the storeNulls. - length = (fields = insertStmt.fields).length; - paramIndexes = insertStmt.paramIndexes; - record = insertStmt.record; - } - else // Update statement. - { - nulls = (updateStmt = (SQLUpdateStatement)stmt).storeNulls; // Cleans the storeNulls. - length = (fields = updateStmt.fields).length; - paramIndexes = updateStmt.paramIndexes; - record = updateStmt.record; - } - - if (fields[0] == null) - fields[0] = "rowid"; // Inserts the rowid. - - SQLValue[] outRecord = new SQLValue[columnCount]; - IntHashtable hashTable = htName2index; - - Convert.fill(tableNulls, 0, tableNulls.length, 0); // Cleans the storeNulls. - - // juliana@230_9: solved a bug of prepared statement wrong parameter dealing. - while (++i < length) // Makes sure the fields are in db creation order. - try - { - outRecord[idx = hashTable.get(fields[i].hashCode())] = record[i]; // Finds the index of the field on the table and reorders the record. - Utils.setBit(tableNulls, idx, (nulls[i >> 3] & (1 << (i & 7))) != 0); - if (record[i] != null && record[i].asString != null && record[i].asString.equals("?")) - paramIndexes[numParams++] = (short)idx; - } - catch (ElementNotFoundException enfe) - { - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + fields[i]); - } - - // Saves the ordered record. - if (isInsert) - { - insertStmt.record = outRecord; - insertStmt.storeNulls = tableNulls; - } - else - { - updateStmt.record = outRecord; - updateStmt.storeNulls = tableNulls; - } - } - - // rnovais@570_75 juliana@220_2 - // juliana@253_8: now Litebase supports weak cryptography. - /** - * Creates the table files and loads its meta data if it was already created. - * - * @param sourcePath The path of the table on disk. - * @param newName The name of the table. - * @param create Indicates if the table is to be created or just opened. - * @param appCrid The application id of the table. - * @param driver The connection with Litebase. - * @param ascii Indicates if the table strings are to be stored in the ascii format or in the unicode format. - * @param crypto Indicates if the table is to be stored encrypted or not. - * @param throwException Indicates that a TableNotClosedException should be thrown. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void tableCreate(String sourcePath, String newName, boolean create, String appCrid, LitebaseConnection driver, boolean ascii, boolean crypto, - boolean throwException) throws IOException, InvalidDateException - { - PlainDB plainDB = db = new PlainDB(newName, sourcePath, create); // Creates or opens the table files. - plainDB.driver = driver; - if (newName != null && (plainDB.db.size != 0 || create)) // The table is already created if the .db is not empty. - { - name = newName; - plainDB.isAscii = ascii; - plainDB.useCrypto = crypto; - if (plainDB.db.size != 0) // If the table is already created, loads its meta data. - tableLoadMetaData(appCrid, sourcePath, throwException); - } - } - - // rnovais@566_10 - /** - * Renames a table. This never happens to be a temporary ResultSet memory table. - * - * @param driver The LitebaseConnection. - * @param oldTableName The old table name. - * @param newTableName The new table name. - * @throws IOException If an internal method throws it. - */ - void renameTable(LitebaseConnection driver, String oldTableName, String newTableName) throws IOException - { - String tableFullName = driver.appCrid + '-' + newTableName; - Index index; - Index[] indices = columnIndices; - String nameIndex, - newFullName; - NormalFile fnodes; - - // Renames the table. - db.rename(tableFullName, driver.sourcePath); - name = tableFullName; - driver.htTables.remove(oldTableName); - driver.htTables.put(newTableName, this); // Adds the new table name to the hash table. - - // Renames the indices. - int i = columnCount; - - while (--i >= 0) - if (indices[i] != null) - { - index = indices[i]; - newFullName = Utils.getFullFileName(nameIndex = (tableFullName + '$') + i, driver.sourcePath); - - (fnodes = index.fnodes).f.rename(newFullName + ".idk"); // Keys. - fnodes.f = new File(newFullName + ".idk", File.READ_WRITE); - index.name = nameIndex; - } - - // juliana@220_17: rename table now renames the composed indices. - i = numberComposedIndices; - ComposedIndex[] compIndices = composedIndices; - while (--i >= 0) - { - newFullName = Utils.getFullFileName(nameIndex = (tableFullName + '&') + (i+1), driver.sourcePath); - index = compIndices[i].index; - (fnodes = index.fnodes).f.rename(newFullName + ".idk"); // Keys. - fnodes.f = new File(newFullName + ".idk", File.READ_WRITE); - index.name = nameIndex; - } - } - - /** - * Renames a column of a table. - * - * @param oldColumn The name of the old column. - * @param newColumn The name of the new column. - * @throws IOException If an internal method throws it. - * @throws DriverException If the old column does not exist or the new column already exists. - */ - void renameTableColumn(String oldColumn, String newColumn) throws IOException, DriverException - { - int oldHashCode = oldColumn.hashCode(); - IntHashtable ht = htName2index; - - // Gets the old column name. It must exist. - int oldIdx = ht.get(oldHashCode, -1); - if (oldIdx == -1) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INVALID_COLUMN_NAME) + oldColumn); - - int newHashCode = newColumn.hashCode(); - - if (ht.exists(newHashCode)) // The new column name can't exist. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_COLUMN_ALREADY_EXIST) + newColumn); - - try // Changes the column information. - { - ht.remove(oldHashCode); - } - catch (ElementNotFoundException exception) {} - ht.put(newHashCode, oldIdx); - columnHashes[oldIdx] = newHashCode; - columnNames[oldIdx] = newColumn; - tableSaveMetaData(Utils.TSMD_EVERYTHING); - } - - /** - * Sets the meta data for a table. - * - * @param names The table column names. - * @param hashes The table column names hash codes. - * @param types The table column types. - * @param sizes The table column sizes. - * @param attrs The table column attributtes. - * @param values The default values of the table columns. - * @param pkCol The table primary key column. - * @param composedPKIdx The composed primary key index in the composed indices. - * @param composedPKColums The table composed primary columns. - * @param ComposedPKColsSize The number of composed primary keys. - * @throws AlreadyCreatedException if the table is already created. - * @throws IOException If an internal method throws it. - */ - void tableSetMetaData(String[] names, int[] hashes, byte[] types, int[] sizes, byte[] attrs, SQLValue[] values, int pkCol, - int composedPKIdx, byte[] composedPKColums, int ComposedPKColsSize) throws AlreadyCreatedException, IOException - { - // Sets the number of columns. - int count = columnCount = hashes.length; - int bytes; - - columnHashes = hashes; // Sets the column hashes. - columnTypes = types; // Sets the column types. - columnSizes = sizes; // Sets the column sizes. - storeNulls = new byte[bytes = (count + 7) >> 3]; // Initializes the arrays for the nulls. - - // The number of bytes necessary to store the nulls. Each column in a table correspond to one bit. - columnNulls[0] = new byte[bytes]; - columnNulls[1] = new byte[bytes]; - - computeColumnOffsets(); // Computes the column offests. - - // Saves the meta data after everything was set. - if (name != null) // juliana@201_14: It is not necessary to save the meta data in the .db for memory tables. - { - if (db.db.size != 0) // The table can't be already created. - throw new AlreadyCreatedException(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_ALREADY_CREATED)); - - version = VERSION; // juliana@230_12 - - columnNames = names; // Sets the column names. - defaultValues = values; // Sets the defaut values. - columnIndices = new Index[count]; // Initializes the indices. - columnAttrs = attrs; // Sets the column attributes. - primaryKeyCol = pkCol; // Primary key column. - composedPK = composedPKIdx; // Composed primary key index. - - // Sets the composed primary key info. - numberComposedPKCols = ComposedPKColsSize; - composedPrimaryKeyCols = composedPKColums; - - gvOlds = new SQLValue[count]; // Stores old values read from the table. - ghas = new byte[count]; // Stores flags from the record. - - // Creates the primary key records. - primaryKeyValues = new SQLValue[count]; - primaryKeyOldValues = SQLValue.newSQLValues(count); - - tableSaveMetaData(Utils.TSMD_EVERYTHING); // Saves the metadata after everything was set. - } - else - columnNulls[2] = new byte[bytes]; - } - - /** - * Creates a result set. - * - * @param whereClause The condition of the query. - * @return The created result set. - */ - ResultSet createResultSet(SQLBooleanClause whereClause) - { - ResultSet rs = new ResultSet(); - rs.pos = -1; // guich@300: beforeFirst(); - rs.table = this; - rs.lastRecordIndex = db.rowCount - 1; - rs.whereClause = whereClause; - return rs; - } - - /** - * Creates a simple result set. - * - * @param whereClause The condition of the query. - * @return The created result set. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - ResultSet createSimpleResultSet(SQLBooleanClause whereClause) throws IOException, InvalidDateException - { - ResultSet rs = new ResultSet(); - rs.pos = -1; // guich@300: beforeFirst(); - rs.table = this; - rs.lastRecordIndex = db.rowCount - 1; - rs.whereClause = whereClause; - if (whereClause != null) // tries to apply the table indexes to generate a bitmap of the rows to be returned. - SQLSelectStatement.generateIndexedRowsMap(new ResultSet[] {rs}, rs.table.numberComposedIndices > 0); - return rs; - } - - /** - * Creates a result set, not used for joins. - * - * @param whereClause The condition of the query. - * @return The created result set. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - ResultSet createResultSetForSelect(SQLBooleanClause whereClause) throws IOException, InvalidDateException - { - // Apply table indices, if any. - ResultSet rs = createSimpleResultSet(whereClause); - rs.columnCount = columnCount; - return rs; - } - - // juliana@201_3: if an index is created after populating or purging the table, its nodes will be full in order to improve its usage and search - // speed. - /** - * Re-builds an index of a table. - * - * @param column The table column number of the index or -1 for a composed index. - * @param composedIndex The composed index to be rebuilt or null in case of a simple index. - * @param isPrimaryKey Indicates that the index is of a primary key. - * @throws DriverException If there is a null in the primary key or a duplicated key. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void tableReIndex(int column, ComposedIndex composedIndex, boolean isPrimaryKey) throws DriverException, IOException, InvalidDateException - { - // Gets the index. - Index index = (composedIndex == null) ? columnIndices[column] : composedIndex.index; - PlainDB plainDb = db; - int numberColumns = index.types.length, // Gets the number of columns. - i = -1, - j, - n = plainDb.rowCount; - short[] offsets = columnOffsets; - byte[] types = index.types; - byte[] columns = (composedIndex != null) ? composedIndex.columns : null; - byte[] nulls = columnNulls[0]; - boolean isDelayed = index.isWriteDelayed; - - index.deleteAllRows(); // Cleans the index values. - index.setWriteDelayed(true); // This makes the index creation faster. - - // rnovais@570_24 juliana@114_9: checks if the column being reindexed is the primary key columns. - ((NormalFile)plainDb.dbo).loadIntoMemory(true); - ((NormalFile)plainDb.db).loadIntoMemory(true); - - try - { - if (index.isOrdered && composedIndex == null) // Simple index using rowid. - { - SQLValue vals[] = SQLValue.newSQLValues(numberColumns); - while (++i < n) - { - plainDb.read(i); // Reads the row. - if (!plainDb.recordNotDeleted()) // Only gets non-deleted records. - continue; - readValue(vals[0], offsets[0], SQLElement.INT, false, false); // juliana@220_3 juliana@230_14 - index.indexAddKey(vals, i); - } - } - else - { - int rows = plainDb.rowCount; // juliana@284_1: Solved a possible application crash when recreating indices. - SQLValue[][] vals = new SQLValue[rows][]; - int k = 0; - boolean isNull; - - while (++i < n) - { - isNull = false; // Resets the null info. - plainDb.read(i); // Reads the row. - if (!plainDb.recordNotDeleted()) // Only gets non-deleted records. - continue; - readNullBytesOfRecord(0, false, 0); // juliana@201_22: the null columns information wasn't being read when re-creating an index. - vals[k] = SQLValue.newSQLValues(numberColumns); - - if (composedIndex == null) - { - // juliana@230_14 - // juliana@220_3 - // juliana@202_12: Corrected null values dealing when building an index. - readValue(vals[k][0], offsets[column], types[0], isNull = (nulls[column >> 3] & (1 << (column & 7))) != 0, false); - - - // The primary key can't be null. - // juliana@202_10: Corrected a bug that would cause a DriverException if there was a null in an index field when creating it after - // the table is populated. - if (isPrimaryKey && isNull) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PK_CANT_BE_NULL)); - } - else - { - j = numberColumns; - while (--j >= 0) - { - // juliana@230_14 - // juliana@220_3 - // juliana@202_12: Corrected null values dealing when building an index. - readValue(vals[k][j], offsets[columns[j]], types[j], - isNull |= (nulls[columns[j] >> 3] & (1 << (columns[j] & 7))) != 0, false); - - // The primary key can't have a null. - // juliana@202_10: Corrected a bug that would cause a DriverException if there was a null in an index field when creating it - // after the table is populated. - if (isPrimaryKey && isNull) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PK_CANT_BE_NULL)); - } - } - - if (isNull) // Do not store null records. - rows--; - else // juliana@212_2: DATETIME indices would be recreated incorrectly on desktop and BlackBerry. - if (types[0] == SQLElement.LONG) // The record value is stored in an empty field of the first record column value. - vals[k++][0].asInt = i; - else - vals[k++][0].asLong = i; - } - - rows = k; // juliana@270_22: solved a possible crash when the table is corrupted on Android and possibly on other platforms. - if (!index.isOrdered) - { - // A radix sort is done for integer types. It is much more efficient than quick sort. - if (numberColumns == 1 - && (types[0] == SQLElement.SHORT || types[0] == SQLElement.INT || types[0] == SQLElement.LONG || types[0] == SQLElement.DATE)) - radixSort(vals, types[0], new SQLValue[rows][]); - - else - sortRecords(vals, types, 0, rows - 1); - index.isOrdered = true; // The index elements will be inserted in the right order. - } - int count = -1, - compare; - while (++count < rows) - { - // if it is the primary key, checks first if there is violation. - if (isPrimaryKey && count > 0 && ((compare = compareRecords(vals[count], vals[count - 1], types)) == 0 - || compare == Convert.MAX_INT_VALUE || compare == Convert.MIN_INT_VALUE)) - throw new PrimaryKeyViolationException(LitebaseMessage.getMessage(LitebaseMessage.ERR_STATEMENT_CREATE_DUPLICATED_PK) + name); - - if (types[0] == SQLElement.LONG) - index.indexAddKey(vals[count], vals[count][0].asInt); - else - index.indexAddKey(vals[count], (int)vals[count][0].asLong); - if (count > 0) - vals[count - 1] = null; - } - vals = null; - - // An index beggining with rowid is always ordered. - if (composedIndex == null || composedIndex.columns[0] != 0) - index.isOrdered = false; - } - } - finally - { - index.setWriteDelayed(isDelayed); // Uses the user desired delayed settings again. - ((NormalFile)plainDb.dbo).loadIntoMemory(false); - ((NormalFile)plainDb.db).loadIntoMemory(false); - } - } - - // juliana@230_14 - // juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. - /** - * Reads a value from a table. - * - * @param value The value to be read. - * @param offset The offset of the value in its row. - * @param colType The type of the value. - * @param isNull Indicates if the value is null. - * @param isTempBlob Indicates if the blob is not to be loaded on memory. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void readValue(SQLValue value, int offset, int colType, boolean isNull, boolean isTempBlob) throws IOException, InvalidDateException - { - PlainDB plainDB = db; - ByteArrayStream bas = plainDB.bas; - bas.skipBytes(offset); // Skips the first columns. - offset = plainDB.readValue(value, offset, colType, plainDB.basds, name == null, isNull, isTempBlob); // Reads the value - bas.skipBytes(-offset); // Returns to the first column. - } - - /** - * Creates a simple index for the table for the given column. - * - * @param fullTableName The table disk name. - * @param column The column of the index. - * @param columnSizes The sizes of the columns. - * @param columnTypes The types of the columns. - * @param crid The application id of the table. - * @param sourcePath The folder where the table files are stored. - * @param exist Indicates that the index files already exist. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void indexCreateIndex(String fullTableName, int column, int[] columnSizes, byte[] columnTypes, - String crid, String sourcePath, boolean exist) throws IOException, InvalidDateException - { - String name = (fullTableName + '$') + column; // The index name. - - // Creates a new index structure. - Index index = columnIndices[column] = new Index(this, columnTypes, columnSizes, name, sourcePath, exist); - - index.isOrdered = (column == 0); // rowid is always an ordered index. - - columnAttrs[column] |= Utils.ATTR_COLUMN_HAS_INDEX; - } - - /** - * Creates a composed index for a given table. - * - * @param fullTableName The table disk name. - * @param columnIndices he columns of the index. - * @param columnSizes The sizes of the columns. - * @param columnTypes The types of the columns. - * @param newIndexNumber An id for the composed index. - * @param isPK Indicates if the composed index is a composed primary key. - * @param crid The application id of the table. - * @param increaseArray Indicates if the composed indices array must be increased. - * @param sourcePath The folder where the table files are stored. - * @param exist Indicates that the index files already exist. - * @throws DriverException If the maximum number of composed indices was achieved. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void indexCreateComposedIndex(String fullTableName, byte[] columnIndices, int[] columnSizes, byte[] columnTypes, int newIndexNumber, boolean isPK, - String crid, boolean increaseArray, String sourcePath, boolean exist) throws DriverException, IOException, InvalidDateException - { - ComposedIndex ci; - int size = numberComposedIndices; - - String name = (fullTableName + '&') + newIndexNumber; // Passes the newIndex index id. - - if (isPK) // It it is a composed primary key, sets its attribute. - composedPK = newIndexNumber - 1; - - ComposedIndex[] compIndices = composedIndices; - ci = new ComposedIndex(newIndexNumber, columnIndices); - - // juliana@253_9: improved Litebase parser. - - if (increaseArray) - { - compIndices[size] = ci; // New composed index. - numberComposedIndices++; - } - else - compIndices[newIndexNumber - 1] = ci; - ci.index = new Index(this, columnTypes, columnSizes, name, sourcePath, exist); // Creates the index of the composed index. - ci.index.isOrdered = (columnIndices[0] == 0); // The rowid is the column 0. - } - - // Replaces writeRecord(), which received an array of strings. - /** - * Writes a record from an array of values in a result set. - * - * @param values The record to be written in the result set table. - * @throws IOException If an internal method throws it. - */ - void writeRSRecord(SQLValue[] values) throws IOException - { - // Important: this is an optimized version of writeRecord, specially designed to write the values of a resultset. - int n = columnCount, - i; - PlainDB plainDB = db; - DataStreamLB ds = db.basds; // juliana@253_8: now Litebase supports weak cryptography. - int[] sizes = columnSizes; - byte[] types = columnTypes; - byte[] nulls = columnNulls[0]; - - plainDB.add(); // Adds a new row to the result set table. - - // Writes the columns into a temporary buffer. - i = -1; - while (++i < n) - plainDB.writeValue(types[i], values[i], ds, (nulls[i >> 3] & (1 << (i & 7))) == 0, true, sizes[i], 0, true); // juliana@220_3 - - ds.writeBytes(nulls); // Writes the null values. - plainDB.write(); // Finally, writes the row. - } - - /** - * Checks if a primary key constraint was violated - * - * @param val The values inserted in the table. - * @param recPos The position of vals record. - * @param newRecord Indicates if it is an inserted or an updated record. - * @param values An array of values used if the record is not new. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws DriverException If a member of the primary key is null - */ - private void checkPrimaryKey(SQLValue[] vals, int recPos, boolean newRecord, SQLValue[] values) throws IOException, InvalidDateException, DriverException - { - int pKCol = primaryKeyCol; // Simple primary key column. - - - // If the column is a simple primary key, gets the index of its column. Otherwise, gets the index of its composed primary key. - Index index = (pKCol != -1) ? columnIndices[pKCol] : composedIndices[composedPK].index; - - byte[] columns; - boolean hasChanged = false; - byte[] types = index.types; - short[] offsets = columnOffsets; - - if (pKCol == -1) // Gets the columns of the index. - columns = composedIndices[composedPK].columns; - else - columns = new byte[]{(byte)pKCol}; - int i = columns.length; - - if (!newRecord) // An update. - { - db.read(recPos); // Reads the table row. - - while (--i >= 0) - { - // If it is updating a record, reads the old value and checks if a primary key value has changed. - readValue(values[i], offsets[columns[i]], types[i], false, false); // juliana@220_3 juliana@230_14 - - // Tests if the primary key has not changed. - hasChanged |= vals[i] != null && vals[i].valueCompareTo(values[i], types[i], false, false, null) != 0; - - if (vals[i] == null) // Uses the old value. - vals[i] = values[i]; - } - } - - // There can't be a null in a primary key. - i = columns.length; - while (--i >= 0) - if (vals[i] == null) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PK_CANT_BE_NULL)); - - if (hasChanged || newRecord) // Sees if the record does not violate the primary key. - { - index.tempKey.set(vals); - index.getValue(index.tempKey, null); - } - } - - - /** - * Verifies the null and default values of a statement. - * - * @param record The record to be inserted or updated. - * @param storeNullsStmt The storeNulls of the statement. - * @param statementType The type of the statement, which can be SQLElement.STMT_INSERT or - * SQLElement.STMT_UPDATE. - * @throws DriverException If a primary key is or a NOT NULL field is is null. - */ - void verifyNullValues(SQLValue[] record, byte[] storeNullsStmt, int statementType) throws DriverException - { - int len = record.length; - byte[] attrs = columnAttrs; - byte[] nulls = storeNulls; - - if (statementType == SQLElement.CMD_INSERT) // Insert statement. - { - SQLValue[] defaults = defaultValues; - - if (primaryKeyCol != Utils.NO_PRIMARY_KEY && (nulls[primaryKeyCol >> 3] & (1 << (primaryKeyCol & 7))) != 0) // The primary key can't be null. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PK_CANT_BE_NULL)); - - while (--len > 0) // A not null field can't have a null. - if ((record[len] == null || record[len].isNull) && defaults[len] == null && (attrs[len] & Utils.ATTR_COLUMN_IS_NOT_NULL) != 0) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_FIELD_CANT_BE_NULL) + columnNames[len]); - } - else // Update statement. - { - if (primaryKeyCol != Utils.NO_PRIMARY_KEY && (nulls[primaryKeyCol >> 3] & (1 << (primaryKeyCol & 7))) != 0) // The primary key can't be null. - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PK_CANT_BE_NULL)); - while (--len > 0) // If it is to store a null but a null can't be stored. - if ((nulls[len >> 3] & (1 << (len & 7))) != 0 && (attrs[len] & Utils.ATTR_COLUMN_IS_NOT_NULL) != 0) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_FIELD_CANT_BE_NULL) + columnNames[len]); - } - } - - /** - * Writes the records of a result set to a table. - * - * @param list The result set list, one for each table in the from field. - * @param rs2TableColIndexes The mapping between result set and table columns. - * @param selectClause The select clause of the query. - * @param columnIndexesTables Has the indices of the tables for each resulting column. - * @param whereClauseType Indicates the where clause is an AND or an OR. - * @return The total number of records added to the table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - int writeResultSetToTable(ResultSet[] list, short[] rs2TableColIndexes, SQLSelectClause selectClause, Table[] columnIndexesTables, - int whereClauseType) throws IOException, InvalidDateException, InvalidNumberException - { - int count = columnCount, - i = count, - j, - colIndex; - - // rnovais@568_10: when it has an order by table.columnCount = selectClause.fieldsCount + 1. - int totalRecords = 0; - ResultSet rs; - SQLValue[] values = SQLValue.newSQLValues(count); - boolean hasJoin = list.length > 1; - byte[] rsColumnNulls; - byte[] nulls = columnNulls[0]; - - if (!hasJoin) - { - rs = list[0]; - - int size = 0; - int[] sizes = columnSizes; - j = columnCount; - - while (--j >= 0) - if (sizes[j] > 0) - size += 8; - - rsColumnNulls = rs.table.columnNulls[0]; - - // If there's no where clause, allocate at once all the needed records. - // If there are indices, grows the result set table to the number of index records which satisfy the query. - // This reduces a lot the number of "new"s to increase the temporary table. - if (rs.whereClause == null) - { - PlainDB plainDB = db; - - plainDB.rowAvail = (rs.rowsBitmap == null? rs.table.db.rowCount - rs.table.deletedRowsCount : Utils.countBits(rs.rowsBitmap.items)); - plainDB.db.growTo(plainDB.rowAvail++ * plainDB.rowSize); - plainDB.dbo.growTo(plainDB.rowAvail * size); - } - - rs.pos = -1; - - // If rs2TableColIndexes == null, that indicates that the result set and the table have the same sequence of columns. - if (rs2TableColIndexes == null) - while (rs.getNextRecord()) // No preverify needed. - { - rs.table.readNullBytesOfRecord(0, false, 0); // Reads the bytes of the nulls. - Vm.arrayCopy(rsColumnNulls, 0, nulls, 0, rsColumnNulls.length); // Since all the columns match, just copies the nulls. - - i = count; - while (--i >= 0) // Gets the values of the result set columns. - if ((rsColumnNulls[i >> 3] & (1 << (i & 7))) == 0) // If it is null, just skips. - rs.sqlwhereclausetreeGetTableColValue(i, values[i]); - - // Writes the record. - writeRSRecord(values); - totalRecords++; - } - else - { - boolean isNull; - - while (rs.getNextRecord()) // No preverify needed. - { - rs.table.readNullBytesOfRecord(0, false, 0); // Reads the bytes of the nulls. - - i = count; - while (--i >= 0) // Gets the values of the result set columns. - { - // For columns that do not map directly to the underlying table of the result set, just skips the reading. - if (!(isNull = ((colIndex = rs2TableColIndexes[i]) == -1 || (rsColumnNulls[colIndex >> 3] & (1 << (colIndex & 7))) != 0))) - rs.sqlwhereclausetreeGetTableColValue(colIndex, values[i]); - - Utils.setBit(nulls, i, isNull); // Sets the null values for tempTable. - } - - // Writes the record. - writeRSRecord(values); - totalRecords++; - } - } - - if (rs.table.name == null) - rs.table.db = null; - } - else - { - i = rs2TableColIndexes.length; - while (--i >= 0) // join - if (rs2TableColIndexes[i] != -1) // count(*) - { - j = list.length; - while (--j >= 0) - if (columnIndexesTables[i].equals(list[j].table)) - { - list[j].indices.addElement(i); - break; - } - } - return performJoin(list, rs2TableColIndexes, selectClause, values, whereClauseType); - } - - return totalRecords; - } - - /** - * Executes a join operation. - * - * @param list The list of the result sets. - * @param rs2TableColIndexes The mapping between result set and table columns. - * @param selectClause The select clause. - * @param valuesJoin The record to be joined with. - * @param whereClauseType The type of operation used: AND or OR. - * @return The number of records written to the temporary table. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private int performJoin(ResultSet[] list, short[] rs2TableColIndexes, SQLSelectClause selectClause, SQLValue[] valuesJoin, int whereClauseType) throws IOException, InvalidDateException, InvalidNumberException - { - int numTables = list.length, - currentIndexTable = 0, - ret, - totalRecords = 0, - colIndex, - pos, - len; - boolean bitSet; - ResultSet currentRs; - Table table; - boolean[] ignoreWhereCondition = new boolean[numTables]; - byte[] nulls; - - while (currentIndexTable >= 0) - { - currentRs = list[currentIndexTable]; - table = currentRs.table; - nulls = columnNulls[0]; - ret = getNextRecordJoin(list, currentIndexTable, !ignoreWhereCondition[currentIndexTable], whereClauseType); - switch (ret) - { - case VALIDATION_RECORD_OK: - case VALIDATION_RECORD_INCOMPLETE: - table.readNullBytesOfRecord(0, false, 0); - - // Fills the data of the current ResultSet. - len = currentRs.indices.size(); - while (--len >=0) - { - pos = currentRs.indices.items[len]; - - // If rs2TableColIndexes == null, it indicates that the result set and the table have the same sequence of columns. - colIndex = (rs2TableColIndexes == null? len : rs2TableColIndexes[pos]); - - // For columns that do no map directly to the underlying table of the result set, just skips the reading. - bitSet = (table.columnNulls[0][colIndex >> 3] & (1 << (colIndex & 7))) != 0; - if (colIndex != -1 && !bitSet) // If it is null, just skips. - currentRs.sqlwhereclausetreeGetTableColValue(colIndex, valuesJoin[pos]); - - Utils.setBit(nulls, pos, colIndex != -1 && bitSet); // Sets the null values from the temporary table. - - } - if (ret == VALIDATION_RECORD_OK) - { - if (currentIndexTable < numTables - 1) // Goes to the next table. - ignoreWhereCondition[++currentIndexTable] = true; - else - { - // It is the last resultSet, so stores the data. - writeRSRecord(valuesJoin); - totalRecords++; - } - } - else // VALIDATION_RECORD_INCOMPLETE - ignoreWhereCondition[++currentIndexTable] = false; - break; - case NO_RECORD: - currentIndexTable--; - currentRs.pos = -1; // Restarts the current resultset to the next iteration. - } - } - return totalRecords; - } - - /** - * Gets the next record to perform the join operation. - * - * @param list The list of the result sets. - * @param rsIndex The index of the result set of the list used to get the next record. - * @param verifyWhereCondition Indicates if the where clause needs to be verified. - * @param whereClauseType The type of expression in the where clause (OR or AND). - * @return VALIDATION_RECORD_OK, NO_RECORD, VALIDATION_RECORD_NOT_OK, or - * VALIDATION_RECORD_INCOMPLETE. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private int getNextRecordJoin(ResultSet[] list, int rsIndex, boolean verifyWhereCondition, int whereClauseType) throws IOException, - InvalidDateException, InvalidNumberException - { - ResultSet rs = list[rsIndex]; - IntVector auxRowsBitmap = rs.auxRowsBitmap; - int len = list.length, - ret, - last = rs.lastRecordIndex; - PlainDB db = rs.table.db; - SQLBooleanClause whereClause = rs.whereClause; - IntVector rowsBitmap = (auxRowsBitmap != null)? auxRowsBitmap : rs.rowsBitmap; - - if (rowsBitmap != null && verifyWhereCondition) - { - int p; - - int[] items = rowsBitmap.items; - if (rs.pos < last) - if (whereClause == null || auxRowsBitmap != null) // count_index++; - { - // juliana@230_45: join should not take deleted rows into consideration. - // No WHERE clause. Just returns the rows marked in the bitmap. - while ((p = Utils.findNextBitSet(items, rs.pos + 1)) != -1 && p <= last) - { - db.read(rs.pos = p); - if (db.recordNotDeleted()) - { - if (verifyWhereCondition && auxRowsBitmap != null) - return booleanTreeEvaluateJoin(list, (list[rsIndex].whereClause.resultSet - = list[rsIndex]).whereClause.expressionTree); - if (whereClauseType == Utils.WC_TYPE_AND_DIFF_RS) - return (len == rs.indexRs + 1)? VALIDATION_RECORD_OK : VALIDATION_RECORD_INCOMPLETE; - return VALIDATION_RECORD_OK; - } - - } - } - else - // With a remaining WHERE clause there are 2 situations. - // 1) The relationship between the bitmap and the WHERE clause is an AND relationship. - // 2) The relationship between the bitmap and the WHERE clause is an OR relationship. - if (rs.rowsBitmapBoolOp == SQLElement.OP_BOOLEAN_AND) - { - // AND case - Walks through the bits that are set in the bitmap and checks if the rows satisfy the where clause. - // juliana@230_45: join should not take deleted rows into consideration. - while ((p = Utils.findNextBitSet(items, rs.pos + 1)) != -1 && p <= last) - { - db.read(rs.pos = p); - if (db.recordNotDeleted()) - return booleanTreeEvaluateJoin(list, (list[rsIndex].whereClause.resultSet = list[rsIndex]).whereClause.expressionTree); - } - } - else - // OR case - Walks through all records. If the corresponding bit is set in the bitmap, does not need to evaluate WHERE clause. - // Otherwise, checks if row satisfies WHERE clause. - while (rs.pos < last) - { - db.read(++rs.pos); - if (db.recordNotDeleted()) - { - if (rowsBitmap.isBitSet(rs.pos)) - return VALIDATION_RECORD_OK; - if ((ret = booleanTreeEvaluateJoin(list, (list[rsIndex].whereClause.resultSet - = list[rsIndex]).whereClause.expressionTree)) != VALIDATION_RECORD_NOT_OK) - return ret; - } - } - rs.auxRowsBitmap = null; - return NO_RECORD; - } - else - while (rs.pos < last) // count_noindex++; - { - db.read(++rs.pos); - if (db.recordNotDeleted()) - { - if (whereClause == null || !verifyWhereCondition) - return VALIDATION_RECORD_OK; - - // juliana@213_2: corrected a bug that could make joins not work with ORs using indices. - ret = booleanTreeEvaluateJoin(list, (rs.whereClause.resultSet = list[rsIndex]).whereClause.expressionTree); - if (ret == VALIDATION_RECORD_NOT_OK && rs.whereClause.appliedIndexesBooleanOp == SQLElement.OP_BOOLEAN_OR) - while (++rsIndex < len) - if (list[rsIndex].auxRowsBitmap != null || list[rsIndex].rowsBitmap != null) - return VALIDATION_RECORD_INCOMPLETE; - return ret; - } - } - - return NO_RECORD; - } - - /** - * Evaluates an expression tree for a join. - * - * @param list The list of the result sets. - * @param tree The expression tree to be evaluated. - * @return VALIDATION_RECORD_OK, NO_RECORD, VALIDATION_RECORD_NOT_OK, or - * VALIDATION_RECORD_INCOMPLETE. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - private int booleanTreeEvaluateJoin(ResultSet[] list, SQLBooleanClauseTree tree) throws IOException, InvalidDateException, - InvalidNumberException - { - int indexRs = tree.booleanClause.resultSet.indexRs, - indexTree = tree.indexRs; - SQLBooleanClauseTree leftTree = tree.leftTree, - rightTree = tree.rightTree; - - if (indexTree >= 0) // AND, OR and BOOLEAN_NOT have index = -1. - { - if (indexTree < indexRs) // It was avaliated before and can return true. - return VALIDATION_RECORD_INCOMPLETE_OK; - if (indexTree > indexRs) // juliana@211_5: solved a bug with joins which would return more answers than desired. - { - if (tree.bothAreIdentifier && leftTree.indexRs == indexRs) // Fills leftTree.value. - { - ResultSet rsBag = list[rightTree.indexRs]; - int boolOp = rsBag.whereClause.appliedIndexesBooleanOp; - - leftTree.valueJoin = leftTree.getOperandValue(); - if (rightTree.hasIndex && boolOp <= 1) - { - // juliana@225_13: join now behaves well with functions in columns with an index. - SQLBooleanClause booleanClause = tree.booleanClause; - SQLResultSetField[] fieldList = booleanClause.fieldList; - int i = booleanClause.fieldsCount; - - while (--i >= 0) - if (fieldList[i].tableColIndex == rightTree.colIndex && fieldList[i].isDataTypeFunction) - return VALIDATION_RECORD_INCOMPLETE; - - // Despite this is a join the parameter 'false' is sent because this is a simple index calculation. - SQLSelectStatement.computeIndex(list, false, rightTree.indexRs, leftTree.valueJoin, tree.operandType, rightTree.colIndex); - - IntVector auxRowsBitmap = rsBag.auxRowsBitmap; - - // juliana@230_39: join now can be much faster if the query is smartly written. - if (rsBag.rowsBitmap != null && auxRowsBitmap != null && boolOp == 1) - { - SQLSelectStatement.mergeBitmaps(auxRowsBitmap.items, rsBag.rowsBitmap.items, 1); - if (Utils.countBits(auxRowsBitmap.items) == 0) - return VALIDATION_RECORD_NOT_OK; - } - } - } - return VALIDATION_RECORD_INCOMPLETE; - } - } - - // The indexes match, so compare the records. - - switch (tree.operandType) // Checks what is the operand type of the tree. - { - // Relational operand. - case SQLElement.OP_REL_LESS: - case SQLElement.OP_REL_EQUAL: - case SQLElement.OP_REL_GREATER: - case SQLElement.OP_REL_GREATER_EQUAL: - case SQLElement.OP_REL_LESS_EQUAL: - case SQLElement.OP_REL_DIFF: - switch (tree.valueType) // Calls the right operation accordingly to the values type. - { - case SQLElement.SHORT: - case SQLElement.INT: - case SQLElement.LONG: - case SQLElement.FLOAT: - case SQLElement.DOUBLE: - case SQLElement.DATE: - case SQLElement.DATETIME: - return tree.compareNumericOperands()? VALIDATION_RECORD_OK : VALIDATION_RECORD_NOT_OK; - case SQLElement.CHARS: - return tree.compareStringOperands(false)? VALIDATION_RECORD_OK : VALIDATION_RECORD_NOT_OK; - case SQLElement.CHARS_NOCASE: - return tree.compareStringOperands(true)? VALIDATION_RECORD_OK : VALIDATION_RECORD_NOT_OK; - } - - // juliana@201_4: joins with like were returning the opposite result. - // Relational operand. - case SQLElement.OP_PAT_MATCH_LIKE: - case SQLElement.OP_PAT_MATCH_NOT_LIKE: - return tree.matchStringOperands(tree.valueType == SQLElement.CHARS_NOCASE) ? VALIDATION_RECORD_OK : VALIDATION_RECORD_NOT_OK; - - case SQLElement.OP_BOOLEAN_AND: // AND connector. - if (leftTree != null && rightTree != null) // Expects both trees to be not null. - switch (booleanTreeEvaluateJoin(list, leftTree)) - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_NOT_OK; - case VALIDATION_RECORD_INCOMPLETE: - if (booleanTreeEvaluateJoin(list, rightTree) == VALIDATION_RECORD_NOT_OK) // Verifies the right branch. - return VALIDATION_RECORD_NOT_OK; - - // All other results return incomplete because the left side has returned incomplete. - return VALIDATION_RECORD_INCOMPLETE; - - case VALIDATION_RECORD_INCOMPLETE_OK: - case VALIDATION_RECORD_OK: - switch (booleanTreeEvaluateJoin(list, rightTree)) // The left side returned true, so verifies the right branch. - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_NOT_OK; - case VALIDATION_RECORD_INCOMPLETE_OK: - case VALIDATION_RECORD_OK: - return VALIDATION_RECORD_OK; // Both sides returns true. - case VALIDATION_RECORD_INCOMPLETE: - - // If the right side returns incomplete, incomplete must be returned, despite the left side returned OK. - return VALIDATION_RECORD_INCOMPLETE; - } - } - - case SQLElement.OP_BOOLEAN_OR: // OR connector. - if (leftTree != null && rightTree != null) // Expects both trees to be not null. - { - switch (booleanTreeEvaluateJoin(list, leftTree)) - { - case VALIDATION_RECORD_OK: - return VALIDATION_RECORD_OK; // Short circuit. - case VALIDATION_RECORD_INCOMPLETE_OK: - switch (booleanTreeEvaluateJoin(list, rightTree)) // Verifies the right branch. - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_INCOMPLETE_OK; // juliana@263_1: corrected a very old bug in a join with OR. - case VALIDATION_RECORD_INCOMPLETE: - return VALIDATION_RECORD_INCOMPLETE; - case VALIDATION_RECORD_OK: - case VALIDATION_RECORD_INCOMPLETE_OK: - return VALIDATION_RECORD_OK; // The right side returned true. - } - - case VALIDATION_RECORD_INCOMPLETE: - switch (booleanTreeEvaluateJoin(list, rightTree)) // Verifies the right branch. - { - case VALIDATION_RECORD_NOT_OK: - case VALIDATION_RECORD_INCOMPLETE: - return VALIDATION_RECORD_INCOMPLETE; - case VALIDATION_RECORD_OK: - case VALIDATION_RECORD_INCOMPLETE_OK: - return VALIDATION_RECORD_OK; // The right side returned true. - } - - case VALIDATION_RECORD_NOT_OK: - { - // The left side returned false, so continues verifing the right branch. - switch (booleanTreeEvaluateJoin(list, rightTree)) - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_NOT_OK; - - // juliana@270_21: solved a very old join problem when using OR and false constants comparison which would make the join - // return no results. - case VALIDATION_RECORD_INCOMPLETE_OK: - return VALIDATION_RECORD_INCOMPLETE_OK; - case VALIDATION_RECORD_OK: - return VALIDATION_RECORD_OK; // Ther right side returned true. - case VALIDATION_RECORD_INCOMPLETE: - return VALIDATION_RECORD_INCOMPLETE; - } - } - } - } - - // juliana@214_4: nots were removed. - - // IS and IS NOT. - case SQLElement.OP_PAT_IS: - case SQLElement.OP_PAT_IS_NOT: - return tree.compareNullOperands()? VALIDATION_RECORD_OK : VALIDATION_RECORD_NOT_OK; - } - - return VALIDATION_RECORD_INCOMPLETE; - } - - /** - * Goes to the end of the record, reads the null bytes, and stores them in table.columnNulls. After this, returns the datastream cursor to the - * previous offset. - * - * @param whichColumnNull The column null index. It ranges from 0 to 3. - * @param dataStreamIsDislocated Indicates if the stream pointer is not pointing to its beginning, - * @param col The column index where the stream pointer is pointing to. - * @throws IOException If an internal method throws it. - */ - void readNullBytesOfRecord(int whichColumnNull, boolean dataStreamIsDislocated, int col) throws IOException - { - DataStreamLB ds = db.basds; // juliana@253_8: now Litebase supports weak cryptography. - int offset = columnOffsets[columnCount]; - if (dataStreamIsDislocated) - offset -= columnOffsets[col]; - - ds.skipBytes(offset); - byte[] nulls = columnNulls[whichColumnNull]; - - offset += ds.readBytes(nulls, 0, nulls.length); - db.bas.skipBytes(-offset); - } - - // juliana@220_3 - /** - * Sorts a table, using an ORDER BY or GROUP BY clause. - * - * @param groupByClause The group by clause. - * @param orderByClause The order by clause. - * @param driver The connection with Litebase. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void sortTable(SQLColumnListClause groupByClause, SQLColumnListClause orderByClause, LitebaseConnection driver) throws IOException, - InvalidDateException - { - byte[] bufAux = new byte[db.rowSize]; // juliana@114_8 - - // Binds the sort lists to the temp table columns. - if (orderByClause != null) - orderByClause.bindColumnsSQLColumnListClause(htName2index, columnTypes, null); - if (groupByClause != null) - groupByClause.bindColumnsSQLColumnListClause(htName2index, columnTypes, null); - - // Picks one of the Column List clauses as the sort list. - SQLColumnListClause sortListClause = (orderByClause == null? groupByClause : orderByClause); - - int count = columnCount; - - // Quick sorts the table. - quickSort(0, db.rowCount - 1, SQLValue.newSQLValues(count), SQLValue.newSQLValues(count), SQLValue.newSQLValues(count), bufAux, - sortListClause.fieldList, driver); - } - - // juliana@250_1: corrected a possible crash when doing ordering operations. - // juliana@227_10: corrected order by or group by with strings being too slow. - // juliana@220_3 - /** - * Quick sort method used to sort the table. - * - * @param first The first index of this partition. - * @param last The last index of this partition. - * @param pivot The pivot of this partition; - * @param someRecord1 An auxiliar record to avoid re-creating it. - * @param someRecord2 An auxiliar record to avoid re-creating it. - * @param bufAux A buffer to store the records. - * @param fieldList The order of comparison of the fields. - * @param driver The connection with Litebase. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - private void quickSort(int first, int last, SQLValue[] pivot, SQLValue[] someRecord1, SQLValue[] someRecord2, byte[] bufAux, - SQLResultSetField[] fieldList, LitebaseConnection driver) throws IOException, InvalidDateException - { - Random r = new Random(); - int[] intVector = db.driver.nodes; - PlainDB plainDB = db; - byte[] basbuf = db.basbuf; - int rowSize = plainDB.rowSize, - size = 2, - low, - high, - pivotIndex; // guich@212_3: now using random partition (improves worst case 2000x). - byte[] nulls1 = columnNulls[0]; - byte[] nulls2 = columnNulls[1]; - byte[] nulls3 = columnNulls[2]; - String[][] strings = new String[last - first + 1][fieldList.length]; - String[] tempString; - - intVector[0] = first; - intVector[1] = last; - - while (size > 0) // guich@212_3: removed recursion (storing in a IntVector). - { - high = last = intVector[--size]; - low = first = intVector[--size]; - - // juliana@213_3: high can't be equal to low. - pivotIndex = high == low? high : r.between(low, high); // guich@212_3: now using random partition (improves worst case 2000x). - - readRecord(pivot, pivotIndex, 2, driver, fieldList, true, strings); - - while (true) // Finds the partitions. - { - while (high >= low) - { - readRecord(someRecord1, low, 0, driver, fieldList, true, strings); - if (Utils.compareRecords(someRecord1, pivot, nulls1, nulls3, fieldList) >= 0) - break; - low++; - } - - Vm.arrayCopy(basbuf, 0, bufAux, 0, rowSize); // juliana@114_8 - - while (high >= low) - { - readRecord(someRecord2, high, 1, driver, fieldList, true, strings); - if (Utils.compareRecords(someRecord2, pivot, nulls2, nulls3, fieldList) <= 0) - break; - high--; - } - - if (low <= high) - { - // juliana@114_8: optimized the swap of the records. Now the buffer is written at once. - tempString = strings[low]; - strings[low] = strings[high]; - strings[high] = tempString; - plainDB.rewrite(low++); - Vm.arrayCopy(bufAux, 0, basbuf, 0, rowSize); - plainDB.rewrite(high--); - } - else break; - } - - // Sorts the partitions. - if (first < high) - { - intVector[size++] = first; - intVector[size++] = high; - } - if (low < last) - { - intVector[size++] = low; - intVector[size++] = last; - } - } - strings = null; - } - - // juliana@220_3 - /** - * Reads the entire record from a table. - * - * @param record An array where the record filed values will be stored. - * @param recPos The record index. - * @param whichColumnNull Indicates where the nulls will be stored. - * @param driver The connection with Litebase. - * @param fieldList A field list that indicates which fields to read from the table. - * @param isTempBlob Indicates if a blob must be loaded or not. - * @param strings An array of strings if this method is used in a sort. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void readRecord(SQLValue[] record, int recPos, int whichColumnNull, LitebaseConnection driver, SQLResultSetField[] fieldList, boolean isTempBlob, - String[][] strings) throws IOException, InvalidDateException - { - int i = fieldList != null? fieldList.length : columnCount; - PlainDB plainDB = db; - - plainDB.read(recPos); - readNullBytesOfRecord(whichColumnNull, false, 0); // Reads the null bytes of the end of the record. - - byte[] nulls = columnNulls[whichColumnNull]; - short[] offsets = columnOffsets; - byte[] types = columnTypes; - - // juliana@226_12: corrected a bug that could make aggregation function not work properly. - if ((plainDB.basds.readInt() & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_DELETED && name != null) - { - plainDB.bas.skipBytes(-4); - return; - } - plainDB.bas.skipBytes(-4); - - if (fieldList == null) // Reads all columns of the table. - while (--i >= 0) - readValue(record[i], offsets[i], types[i], (nulls[i >> 3] & (1 << (i & 7))) != 0, false); // juliana@230_14 - else // Reads only the columns used during sorting. - { - int j; - while (--i >= 0) - { - // juliana@227_10: corrected order by or group by with strings being too slow. - j = fieldList[i].tableColIndex; - if ((types[j] != SQLElement.CHARS && types[j] != SQLElement.CHARS_NOCASE) || strings[recPos][i] == null) - { - readValue(record[j], offsets[j], types[j], (nulls[j >> 3] & (1 << (j & 7))) != 0, false); // juliana@230_14 - strings[recPos][i] = record[j].asString; - } - else - record[j].asString = strings[recPos][i]; - } - } - } - - /** - * Remaps a table column names, so it uses the alias names of the given field list, instead of the original names. - * - * @param fieldList The field list of the select clause. - */ - void remapColumnsNames2Aliases(SQLResultSetField[] fieldList) - { - IntHashtable tableName2Index = htName2index; - int[] hashes = columnHashes; - int i = fieldList.length; - SQLResultSetField field; - String[] names = columnNames; - if (names == null) - { - names = columnNames = new String[i]; - - while (--i >= 0) - { - names[i] = (field = fieldList[i]).alias; - if (hashes[i] == field.aliasHashCode) // Replaces the original mapping, if necessary. - continue; - - // rnovais@103_2: changed from columnHashCode to field.aliasHashCode - tableName2Index.put(hashes[i], i); // Already replaces old values. - } - } - } - - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. - /** - * Converts the strings of the record into the real values, accordingly to the given table column types. - * - * @param record The record whose strings are to be transformed in their real types. - * @throws InvalidDateException If an internal method throws it. - * @throws InvalidNumberException If an internal method throws it. - */ - void convertStringsToValues(SQLValue[] record) throws InvalidNumberException, InvalidDateException - { - int i = record.length, - type; - byte[] types = columnTypes; - String strVal; - - while (--i > 0) // 0 = rowid. - { - // If the column is storing a null, the string is considered to be null. - strVal = (record[i] != null ? record[i].asString : null); - - // A blob can't be set in a normal statement (if the string is not null, it won't be a "?". - if ((type = types[i]) == SQLElement.BLOB && strVal != null && !strVal.equals("?")) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_BLOBS_PREPARED)); - - // Ignores null values, blobs or unset parameters. - if (strVal == null || strVal.equals("?") || type == SQLElement.BLOB) // guich@tc100b4: also continue if its blob. - continue; - - switch (type) - { - case SQLElement.SHORT: // SHORT - record[i].asShort = Convert.toShort(strVal); - break; - - case SQLElement.INT: // INT - record[i].asInt = Convert.toInt(strVal); - break; - - case SQLElement.LONG: // LONG - record[i].asLong = Convert.toLong(strVal); - break; - - case SQLElement.FLOAT: // FLOAT - record[i].asDouble = Utils.toFloat(strVal); - break; - - case SQLElement.DOUBLE: // DOUBLE - record[i].asDouble = Convert.toDouble(strVal); - break; - - case SQLElement.DATE: // DATE - record[i].asInt = db.driver.tempDate.set(strVal.trim(), Settings.DATE_YMD); - break; - - case SQLElement.DATETIME: // DATETIME - record[i].parseDateTime(db.driver.tempDate, strVal); - } - } - } - - /** - * Writes a record on a disk table. - * - * @param values The values to be written on the table. - * @param recPos The record position. - * @throws DriverException If there is a null in the primary key. - * @throws IOException If an internal method throws it. - * @throws InvalidDateException If an internal method throws it. - */ - void writeRecord(SQLValue[] values, int recPos) throws DriverException, IOException, InvalidDateException - { - int n = columnCount, // nowosad@200: the array of values may be larger than necessary; so use the table field count to truncate it. - j, - i = n, - rowid = 0, - writePos = -1, - offset = 2; - ByteArrayStream bas = db.bas; - DataStreamLB ds = db.basds; // juliana@253_8: now Litebase supports weak cryptography. - Index idx; - boolean addingNewRecord = recPos == -1, - changePos = false; - byte[] nulls = storeNulls; - byte[] columnNulls0 = columnNulls[0]; - SQLValue[] defaults = defaultValues; - SQLValue[] auxValues = primaryKeyValues; - SQLValue[] auxOldValues = primaryKeyOldValues; - byte[] attrs = columnAttrs; - PlainDB plainDB = db; - byte[] composedPKCols = composedPrimaryKeyCols; - byte[] types = columnTypes; - int[] sizes = columnSizes; - - bas.reset(); - Convert.fill(columnNulls0, 0, columnNulls0.length, 0); // First of all, clear the columnNulls used. - - while (--i > 0) // 0 = rowid = never is null. - { - // juliana@225_7: a PrimaryKeyViolation was not being thrown when two strings with the same prefix were inserted and the field definition - // had the size of the prefix and a primary key. - if ((types[i] == SQLElement.CHARS || types[i] == SQLElement.CHARS_NOCASE) - && values[i] != null && values[i].asString != null && values[i].asString.length() > sizes[i]) - values[i].asString = values[i].asString.substring(0, sizes[i]); - - if (addingNewRecord) - { - if ((nulls[i >> 3] & (1 << (i & 7))) == 0) // If not explicit to store null. - { - if ((attrs[i] & Utils.ATTR_COLUMN_IS_NOT_NULL) == 0) // Can be null. - { - if (values[i] == null || values[i].isNull) - if (defaults[i] == null) // It doesn't have a default value. - columnNulls0[i >> 3] |= (1 << (i & 7)); // Sets the column as null. - else - values[i] = defaults[i]; // If it doesn't have a value, stores the default value. - - // At this moment, if it can't be null, necessarily it has a default value. - } - else if (values[i] == null || values[i].isNull) - values[i] = defaults[i]; - } - else - columnNulls0[i >> 3] |= (1 << (i & 7)); - } - else // Update Statement. - if ((nulls[i >> 3] & (1 << (i & 7))) != 0) - columnNulls0[i >> 3] |= (1 << (i & 7)); - } - - // If there is a primary key column, there can't be repeated values. - if (primaryKeyCol != Utils.NO_PRIMARY_KEY) // nowosad@200: if table has a primary key, tests if writing the record does not violate it. - { - auxValues[0] = values[primaryKeyCol]; - checkPrimaryKey(auxValues, recPos, addingNewRecord, auxOldValues); - } - - if (composedPKCols != null) - { - i = composedPKCols.length; - while (--i >= 0) // A field of the composed primary key can't be null. - { - j = composedPKCols[i]; - - if ((nulls[j >> 3] & (1 << (j & 7))) != 0 || (values[j] != null && values[j].isNull)) - throw new DriverException(LitebaseMessage.getMessage(LitebaseMessage.ERR_PK_CANT_BE_NULL)); - auxValues[i] = values[composedPKCols[i]]; - } - checkPrimaryKey(auxValues, recPos, addingNewRecord, auxOldValues); - } - - if (addingNewRecord) // Adding a record? - { - plainDB.add(); - writePos = plainDB.rowCount; - values[0].asInt = rowid = currentRowId++; // Writes the rowId, marking the attribute as new. - resetAuxRowId(); - } - else // May have to read the value before deleting the index value. - { - plainDB.read(writePos = recPos); - rowid = ds.readInt(); - bas.reset(); - wasUpdated = true; // juliana@270_27: now purge will also really purge the table if it only suffers updates. - } - - int type; - boolean isNull, - valueOk, - hasvolds; - - byte[] has = ghas; // Stores the 3 attributes for this method. - SQLValue[] vOlds = gvOlds; - Index[] indices = columnIndices; - byte[] columnNulls1 = columnNulls[1]; - byte[] asBlob; - SQLValue[] one = plainDB.driver.oneValue; - - Convert.fill(has, 0, has.length, 0); - - i = -1; - while (++i < n) // 0 = rowid - { - changePos = false; - type = types[i]; - if (isNull = ((columnNulls0[i >> 3] & (1 << (i & 7))) != 0)) - has[i] |= HAS_NULLVAL; - valueOk = (values[i] != null || isNull); - - idx = indices[i]; // If a new value is being written, the table index (if any) needs to be updated. - - if (valueOk && idx != null) // Only this row is being updated (valueOk). - has[i] |= HAS_IDX; - - if (!addingNewRecord) // Reads the previous value if it is an update. - { - // Verifies if the value to be read is null. IMPORTANT: the array of bytes can't be stored in table.columnNulls[0]. This variable is in - // use. Then, it will be read in Table.columnNulls[1]. - readNullBytesOfRecord(1, true, i); - - has[i] |= (columnNulls1[i >> 3] & (1 << (i & 7))) != 0 ? ISNULL_VOLDS : 0; - if (vOlds[i] == null) - vOlds[i] = new SQLValue(); - else - { - vOlds[i].asString = null; - vOlds[i].asBlob = null; - - } - - vOlds[i].asInt = -1; // This is a flag that indicates that blobs are not to be loaded. - - // The offset is already positioned and is restored after read. - readValue(vOlds[i], 0, type, (has[i] & ISNULL_VOLDS) != 0, false); // juliana@220_3 juliana@230_14 - - if (valueOk && type == SQLElement.BLOB) - { - // juliana@202_21: Always writes the string at the end of the .dbo. This removes possible bugs when doing updates. - // A blob in the .dbo must have its position changed if the the new value is greater than the old one. - j = ((asBlob = vOlds[i].asBlob) == null)? vOlds[i].asInt : asBlob.length; - if (values[i] != null && values[i].asBlob != null - && (((asBlob != null || vOlds[i].asInt != -1) && j < values[i].asBlob.length) || asBlob == null)) - changePos = true; - else if (asBlob != null || vOlds[i].asInt != -1) - offset = j + 4; - } - } - - // Writes the value. - plainDB.writeValue(type, values[i], ds, valueOk && !isNull, addingNewRecord || changePos, sizes[i], offset, false); // juliana@220_3 - - // If the new and the old values are null and the column is not the rowid, sets the value as null. - if (i > 0 && values[i] == null && (has[i] & ISNULL_VOLDS) != 0) - columnNulls0[i >> 3] |= (1 << (i & 7)); - } - - ds.writeBytes(columnNulls0, 0, columnNulls0.length); // After the columns, stores the bytes of the null values. - - // juliana@230_12: improved recover table to take .dbo data into consideration. - // juliana@220_4: added a crc32 code for every record. Please update your tables. - byte[] buffer = bas.getBuffer(); - byte[] byteArray; - - j = buffer[3]; // juliana@270_23: Corrected a RowIterator bug of an update changing an updated row to synced again. - buffer[3] = (plainDB.useCrypto? (byte)0xAA : 0); // juliana@222_5: The crc was not being calculated correctly for updates. - int crc32 = updateCRC32(buffer, bas.getPos(), 0, plainDB.useCrypto); - - if (version == Table.VERSION) - { - int[] intArray = oneInt; - - i = n; - while (--i > 0) - if (types[i] == SQLElement.CHARS || types[i] == SQLElement.CHARS_NOCASE) - { - if (values[i] != null && !values[i].isNull) - { - byteArray = Utils.toByteArray(values[i].asString); - crc32 = Table.updateCRC32(byteArray, byteArray.length, crc32, false); - } - else if (!addingNewRecord && (values[i] == null || !values[i].isNull) && vOlds[i] != null && !vOlds[i].isNull && vOlds[i].asString != null) - { - byteArray = Utils.toByteArray(vOlds[i].asString); - crc32 = Table.updateCRC32(byteArray, byteArray.length, crc32, false); - } - } - else if (types[i] == SQLElement.BLOB) - { - if (values[i] != null && !values[i].isNull) - { - // juliana@239_4: corrected a non-desired possible row delete when recovering a table with blobs. - intArray[0] = ((values[i].asBlob.length > sizes[i])? sizes[i] : values[i].asBlob.length); - crc32 = Table.updateCRC32(Convert.ints2bytes(intArray, 4), 4, crc32, false); - } - // juliana@265_2: corrected a problem where a row could be wrongly deleted by recovering a table when an update was done and the - // column which was of type blob remained null on Java SE and BlackBerry. - else if (!addingNewRecord && (values[i] == null || !values[i].isNull) && vOlds[i] != null && !vOlds[i].isNull && vOlds[i].asInt != -1) - { - intArray[0] = vOlds[i].asInt; - crc32 = Table.updateCRC32(Convert.ints2bytes(intArray, 4), 4, crc32, false); - } - } - } - ds.writeInt(crc32); // Computes the crc for the record and stores at the end of the record. - buffer[3] = (byte)j; // juliana@270_23: Corrected a RowIterator bug of an update changing an updated row to synced again. - - if (rowid > 0) // Now the record's attribute has to be updated. - { - bas.reset(); - ds.writeInt(addingNewRecord? (rowid & Utils.ROW_ID_MASK) | Utils.ROW_ATTR_NEW : rowUpdated(rowid)); // Sets the row as new or updated. - } - - // Writes the row. - if (addingNewRecord) - plainDB.write(); - else - plainDB.rewrite(writePos); - - while (--n >= 0) // Finally, adds the values to the indices. // 0 = rowid - - if ((has[n] & HAS_IDX) != 0) - { - idx = indices[n]; - - isNull = (columnNulls0[n >> 3] & (1 << (n & 7))) != 0; - if (addingNewRecord) - { - if (!isNull) // Doesn't store null values on indices. - { - one[0] = values[n]; - idx.indexAddKey(one, writePos); - } - } - else // Updating key? Removes the old one and adds the new one. - if (values[n].valueCompareTo(vOlds[n], types[n], isNull, hasvolds = (has[n] & ISNULL_VOLDS) != 0, null) != 0) - { - if (!hasvolds) - { - one[0] = vOlds[n]; // Encapsulates - idx.tempKey.set(one); - idx.removeValue(idx.tempKey, writePos); - } - - if (!isNull) // If it is updating a 'non-null value' to 'null value', only removes it. - { - one[0] = values[n]; - idx.indexAddKey(one, writePos); - } - } - } - - if ((i = numberComposedIndices) > 0) // Fills the composed indices. - { - // If a composed index has all column values equal to null, it is not possible to store this row in the index. Composed indices that have at - // least one field that is not null can be stored in the index, but the null values must be handled. This implies in changing the index - // format. Maybe using the same way like null values are handled on tables. This certainly will decrease the index performance. For - // simplicity, the key will be stored in a composed index only if all values are not null. This is a project choice. - int size, - column; - boolean store, - remove, // juliana@230_43 - change; // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - ComposedIndex ci; - SQLValue[] vals = null; - SQLValue[] valsRev = null; - byte[] columns; - ComposedIndex[] compIndices = composedIndices; - Index index; - - while (--i >= 0) - { - size = (columns = (ci = compIndices[i]).columns).length; - if (ci.indexId > 0) - { - if (vals == null || vals.length < size) - { - vals = new SQLValue[size]; - valsRev = new SQLValue[size]; - } - store = remove = true; - change = false; - j = size; - while (--j >= 0) - { - if ((has[column = columns[j]] & HAS_NULLVAL) != 0) // Only stores non-null values. - store = false; - - // juliana@230_43: solved a possible exception if updating a table with composed indices and nulls. - if ((has[column] & ISNULL_VOLDS) != 0) - remove = false; - - // Sets the old and new index values. - // juliana@282_2: Doesn't update the composed index or PK if the old values are the same of the new ones. - if (values[column] == null) // juliana@201_18: can't reuse values. Otherwise, it will spoil the next update. - vals[j] = vOlds[column]; - else - { - if (addingNewRecord || values[column].valueCompareTo(vOlds[column], types[column], !remove, !store, null) != 0) - change = true; - vals[j] = values[column]; - } - - valsRev[j] = vOlds[column]; - } - - index = ci.index; - - // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - // juliana@230_43: solved a possible exception if updating a table with composed indices and nulls. - if (!addingNewRecord && remove && change) // Removes the old composed index entry. - { - index.tempKey.set(valsRev); - index.removeValue(ci.index.tempKey, writePos); - } - - // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - if (store && change) - index.indexAddKey(vals, writePos); - } - } - } - - // juliana@227_3: improved table files flush dealing. - // juliana@270_25: corrected a possible lose of records in recover table when 10 is passed to LitebaseConnection.setRowInc(). - NormalFile dbFile = (NormalFile)plainDB.db, - dboFile = (NormalFile)plainDB.dbo; - if (!dbFile.dontFlush) // juliana@202_23: Flushs the files to disk when row increment is the default. - { - if (dbFile.cacheIsDirty) - dbFile.flushCache(); // Flushs .db. - if (dboFile.cacheIsDirty) - dboFile.flushCache(); // Flushs .dbo. - } - } - - // juliana@230_12: improved recover table to take .dbo data into consideration. - // juliana@220_4: added a crc32 code for every record. Please update your tables. - /** - * Updates the CRC32 value with the values of the given buffer. - * - * @param buffer The buffer. - * @param length The number of bytes to be used to update the CRC code. - * @param oldCRC The previous CRC32 value. - * @param useCrypto Indicates if cryptography is to be used or not. - * @return The CRC32 code updated to include the buffer data. - */ - static int updateCRC32(byte[] buffer, int length, int oldCRC, boolean useCrypto) - { - int[] crcTable = CRC32Stream.crcTable; - int offset = 0; - - oldCRC = ~oldCRC; - if (useCrypto) - while (--length >= 0) - oldCRC = crcTable[(oldCRC ^ (buffer[offset++] ^ 0xAA)) & 0xff] ^ (oldCRC >>> 8); - else - while (--length >= 0) - oldCRC = crcTable[(oldCRC ^ buffer[offset++]) & 0xff] ^ (oldCRC >>> 8); - - return ~oldCRC; - } - - /** - * Resets the auxiliary rowid. - * - * @throws IOException If an internal method throws it. - */ - private void resetAuxRowId() throws IOException // rnovais@570_61 - { - if (auxRowId != Utils.ATTR_DEFAULT_AUX_ROWID) - { - XFile dbFile = db.db; - int pos = dbFile.pos; - - auxRowId = Utils.ATTR_DEFAULT_AUX_ROWID; - tableSaveMetaData(Utils.TSMD_ONLY_AUXROWID); - dbFile.setPos(pos); - } - } - - /** - * Changes the state of a row to updated. - * - * @param id The rowid to have its atribute changed. - * @return The rowid with its atribute changed to updated. - */ - private int rowUpdated(int id) - { - if ((id & Utils.ROW_ATTR_MASK) == Utils.ROW_ATTR_SYNCED) - return (id & Utils.ROW_ID_MASK) | Utils.ROW_ATTR_UPDATED; // Sets the row as update. - return id; - } - - /** - * Compares two records. Used for sorting the table to build the indices from scratch. - * - * @param vals1 The first record of the comparison. - * @param vals2 The second record of the comparison. - * @param types The types of the record values. - * @return A positive number if vals1 > vals2; 0 if vals1 == vals2; -1, otherwise. It will return MAX_INT_VALUE if both records are - * equal but the record of the first is greater than the second, and MIN_INT_VALUE if both records are equal but the record of the - * first is less than the second. - * @throws IOException If an internal method throws it. - */ - private static int compareRecords(SQLValue[] vals1, SQLValue[] vals2, byte[] types) throws IOException - { - int n = vals1.length, - i = -1, - result; - - while (++i < n) // Does the comparison between the values till one of them is different from zero. - if ((result = vals1[i].valueCompareTo(vals2[i], types[i], false, false, null)) != 0) - return result; - if (types[0] != SQLElement.LONG) - { - if (vals1[0].asLong > vals2[0].asLong) - return Convert.MAX_INT_VALUE; - if (vals1[0].asLong < vals2[0].asLong) - return Convert.MIN_INT_VALUE; - return 0; - } - if (vals1[0].asInt > vals2[0].asInt) - return Convert.MAX_INT_VALUE; - if (vals1[0].asInt < vals2[0].asInt) - return Convert.MIN_INT_VALUE; - return 0; - } - - /** - * Quick sort used for sorting the table to build the indices from scratch. This one is simpler than the sort used for order / gropu by. - * Uses a stack instead of a recursion. - * - * @param sortValues The records to be sorted. - * @param types The types of the record values. - * @param first The first element of current partition. - * @param last The last element of the current. - * @throws IOException If an internal method throws it. - */ - private void sortRecords(SQLValue[][] sortValues, byte[] types, int first, int last) throws IOException - { - // guich@212_3: checks if the values are already in order. - SQLValue[] tempValues; - int i = first; - while (++i <= last) - if (compareRecords(sortValues[i - 1], sortValues[i], types) > 0) - break; - if (i > last) - return; - - // juliana@250_1: corrected a possible crash when doing ordering operations. - // Not fully sorted. - int size = 2, - low, - high; - int[] intVector = db.driver.nodes; - Random r = new Random(); - SQLValue[] mid; - - intVector[0] = first; - intVector[1] = last; - while (size > 0) // guich@212_3: removed recursion (storing in a IntVector). - { - high = last = intVector[--size]; - low = first = intVector[--size]; - - // juliana@213_3: high can't be equal to low. - mid = sortValues[high == low? high : r.between(low, high)]; // guich@212_3: now using random partition (improves worst case 2000x). - - while (true) // Finds the partitions. - { - while (high >= low && compareRecords(mid, sortValues[low], types) > 0) - low++; - while (high >= low && compareRecords(mid, sortValues[high], types) < 0) - high--; - - if (low <= high) - { - tempValues = sortValues[low]; - sortValues[low++] = sortValues[high]; - sortValues[high--] = tempValues; - } - else break; - } - - // Sorts the partitions. - if (first < high) - { - intVector[size++] = first; - intVector[size++] = high; - } - if (low < last) - { - intVector[size++] = low; - intVector[size++] = last; - } - } - } - - /** - * Does a radix sort on the given SQLValue array. Only integral types are allowed (SHORT, INT, LONG). This is faster than quicksort. Also used to - * build the indices from scratch. - * - * @param source The values to be sorted. Only simple records for simple indices can be used. - * @param type The type of the elements. - * @param temp A temporary array for the sort. - */ - private static void radixSort(SQLValue source[][], int type, SQLValue[][] temp) - { - int count[] = new int[256]; - int index[] = new int[256]; - SQLValue z[][]; - int byteCount = (type == SQLElement.INT)? 4 : (type == SQLElement.SHORT) ? 2 : 8, - i = 0, - length = temp.length; // juliana@227_15: corrected a possible NullPointerException when creating an index in a column with null values. - long mask = 0xFF, - bits = radixPass(0, source, temp, count, index, type, length); - - while (++i < byteCount) - { - if ((bits & (mask <<= 8)) != 0) // Any bits in this range? - { - // Swaps the from/to arrays. - z = source; - source = temp; - temp = z; - radixPass(i, source, temp, count, index, type, length); // Yes, sort. - } - } - if (temp != source) // If the final sorted array is not at the source, copies to it. - Vm.arrayCopy(temp, 0, source, 0, length); - } - - /** - * Executes a pass of the radix sort. - * - * @param start Start bit. - * @param source The source array, - * @param dest The dest array where the operations with the source are copied to. - * @param count A temporary array. - * @param index A temporary array. - * @param type The type of the values being sorted. - * @param length The number of rows to be sorted. - * @return A number of bits. - */ - private static long radixPass(int start, SQLValue source[][], SQLValue dest[][], int[] count, int[] index, int type, int length) - { - int i = 0, - n = length, // juliana@227_15: corrected a possible NullPointerException when creating an index in a column with null values. - ibits = 0, - b, - ishift = start << 3; - long lbits = 0, - lshift = start << 3, - lb; - - if (start > 0) - Convert.fill(count, 0, 255, 0); - - switch (type) - { - case SQLElement.INT: - case SQLElement.DATE: // juliana@214_9: index creation for date types could create corrupted indices. - if (start == 0) - while (--n >= 0) - { - count[(b = source[i++][0].asInt) & 0xFF]++; - ibits |= b; - } - else - if (start == 3) - while (--n >= 0) - count[(source[i++][0].asInt >> ishift) + 128]++; - else - while (--n >= 0) count[(source[i++][0].asInt >> ishift) & 0xFF]++; - break; - case SQLElement.SHORT: - if (start == 0) - while (--n >= 0) - { - count[(b=source[i++][0].asShort) & 0xFF]++; - ibits |= b; - } - else - while (--n >= 0) - count[(source[i++][0].asShort >> ishift) + 128]++; - break; - case SQLElement.LONG: - if (start == 0) - while (--n >= 0) - { - count[(int)((lb=source[i++][0].asLong) & 0xFF)]++; - lbits |= lb; - } - else - if (start == 7) - while (--n >= 0) - count[(int)((source[i++][0].asLong >> lshift) + 128)]++; - else - while (--n >= 0) - count[(int)((source[i++][0].asLong >> lshift) & 0xFF)]++; - } - - index[0] = i = 0; - n = 255; - while (--n >= 0) - { - index[i + 1] = index[i] + count[i]; - i++; - } - - i = 0; - n = length; - switch (type) - { - case SQLElement.INT: - case SQLElement.DATE: // juliana@214_9: index creation for date types could create corrupted indices. - if (start == 0) - while (--n >= 0) - { - dest[index[(source[i][0].asInt) & 0xFF]++] = source[i]; - i++; - } - else if (start == 3) - while (--n >= 0) - { - dest[index[(source[i][0].asInt >> ishift) + 128]++] = source[i]; - i++; - } - else - while (--n >= 0) - { - dest[index[(source[i][0].asInt >> ishift) & 0xFF]++] = source[i]; - i++; - } - break; - case SQLElement.SHORT: - if (start == 0) - while (--n >= 0) - { - dest[index[(source[i][0].asShort) & 0xFF]++] = source[i]; - i++; - } - else - while (--n >= 0) - { - dest[index[(source[i][0].asShort >> ishift) + 128]++] = source[i]; - i++; - } - break; - case SQLElement.LONG: - if (start == 0) - while (--n >= 0) - { - dest[index[(int)((source[i][0].asLong) & 0xFF)]++] = source[i]; - i++; - } - else if (start == 7) - while (--n >= 0) - { - dest[index[(int)((source[i][0].asLong >> lshift) + 128)]++] = source[i]; - i++; - } - else - while (--n >= 0) - { - dest[index[(int)((source[i][0].asLong >> lshift) & 0xFF)]++] = source[i]; - i++; - } - } - - return type == SQLElement.LONG? lbits : ibits; - } - - // guich@201_9: always shrink the .db and .dbo memory files. - /** - * Compresses the buffer at the current position. - * - * @throws IOException If an internal method throws it. - */ - void plainShrinkToSize() throws IOException - { - PlainDB plainDB = db; - MemoryFile dbFile = (MemoryFile)plainDB.db, - dboFile = (MemoryFile)plainDB.dbo; - - if (plainDB.rowCount > 0 && plainDB.rowAvail > 0) - { - int ret = plainDB.rowCount * plainDB.rowSize; - if (dbFile.size != ret) - { - dbFile.shrinkTo(ret); - dbFile.size = ret; - plainDB.rowAvail = 0; - } - if (dboFile.size != dboFile.finalPos) - { - dboFile.shrinkTo(dboFile.finalPos); // guich@201: also shrinks the .dbo, - dboFile.size = dboFile.finalPos; - } - } - } - - /** - * Changes a table to the modified state whenever it is modified. - * - * @throws IOException If an internal method throws it. - */ - void setModified() throws IOException - { - PlainDB plainDB = db; - NormalFile dbFile = (NormalFile)plainDB.db; - byte[] oneByte = plainDB.driver.oneByte; - - dbFile.setPos(6); - - // juliana@230_13: removed some possible strange behaviours when using threads. - oneByte[0] = (byte)(plainDB.isAscii? Table.IS_ASCII : 0); - if (plainDB.useCrypto) // juliana@253_8: now Litebase supports weak cryptography. - oneByte[0] = oneByte[0] ^= 0xAA; - dbFile.writeBytes(oneByte, 0, 1); - - dbFile.flushCache(); - isModified = true; - } -} diff --git a/LitebaseSDK/src/java/litebase/TableNotClosedException.java b/LitebaseSDK/src/java/litebase/TableNotClosedException.java deleted file mode 100644 index a818aa6c57..0000000000 --- a/LitebaseSDK/src/java/litebase/TableNotClosedException.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_2: added TableNotClosedException which will be raised whenever a table is not closed properly. -/** - * This exception may be thrown if a table was not closed properly. - * - * @see LitebaseConnection#recoverTable(String) - */ -@Deprecated -public class TableNotClosedException extends RuntimeException -{ - /** - * Constructs a new TableNotClosedException exception with the specified detail message. - * - * @param tableName The name of the table not closed properly. - */ - TableNotClosedException(String tableName) - { - super(LitebaseMessage.getMessage(LitebaseMessage.ERR_TABLE_NOT_CLOSED) + tableName + '.'); - } - -} diff --git a/LitebaseSDK/src/java/litebase/TableNotClosedException4D.java b/LitebaseSDK/src/java/litebase/TableNotClosedException4D.java deleted file mode 100644 index 8f7a567615..0000000000 --- a/LitebaseSDK/src/java/litebase/TableNotClosedException4D.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -// juliana@220_2: added TableNotClosedException which will be raised whenever a table is not closed properly. -/** - * This exception may be thrown if a table was not closed properly. - * - * @see LitebaseConnection#recoverTable(String) - */ -@Deprecated -public class TableNotClosedException4D extends RuntimeException -{ -} diff --git a/LitebaseSDK/src/java/litebase/Utils.java b/LitebaseSDK/src/java/litebase/Utils.java deleted file mode 100644 index ab95e0341b..0000000000 --- a/LitebaseSDK/src/java/litebase/Utils.java +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.IOException; -import totalcross.sys.*; - -/** - * This class has useful constants, fields, and methods for the other Litebase classes. - */ -@Deprecated -class Utils -{ - /** - * The default row increment, that is, how many empty rows are inserted in a .db file at once. - */ - static final int DEFAULT_ROW_INC = 10; - - // juliana@114_9: the absence of primary key can't be zero because the rowid may be a primary key or it can have an index. - /** - * Indicates if table has primary key. - */ - static final int NO_PRIMARY_KEY = -1; - - // rnovais@567_2: added more sizes. - /** - * The sizes of each type in the record in the .db file. - */ - static final byte[] typeSizes = {4, 2, 4, 8, 4, 8, 4, -1, 4, 8, 4}; - - /** - * Indicates if the column has an index. - */ - static final int ATTR_COLUMN_HAS_INDEX = 1; - - /** - * Indicates if a row has an auxiliary rowid. - */ - static final int ATTR_DEFAULT_AUX_ROWID = -1; // rnovais@570_61 - - /** - * Indicates if the column has a default value. - */ - static final int ATTR_COLUMN_HAS_DEFAULT = 2; - - /** - * Indicates if a column is defined as NOT NULL. - */ - static final int ATTR_COLUMN_IS_NOT_NULL = 4; - - // juliana@253_5: removed .idr files from all indices and changed its format. - - /** - * When saving the meta data, indicates that only the deleted rows count was changed. - */ - static final int TSMD_ONLY_DELETEDROWSCOUNT = 1; - - /** - * When saving the meta data, indicates that only the primary key was changed. - */ - static final int TSMD_ONLY_PRIMARYKEYCOL = 2; - - /** - * When saving the meta data, indicates that everything must be saved. - */ - static final int TSMD_EVERYTHING = 3; - - /** - * When saving the meta data, indicates that only the auxiliary rowid was changed. - */ - static final int TSMD_ONLY_AUXROWID = 4; - - // guich@300: added support for basic synchronization. - /** - * Indicates if a row is synced. - */ - static final int ROW_ATTR_SYNCED = 0X00000000; // 0 - - /** - * Indicates if a row is new. - */ - static final int ROW_ATTR_NEW = 0X40000000; // 1 - - /** - * Indicates if a row is updated. - */ - static final int ROW_ATTR_UPDATED = 0x80000000; // 2 - - /** - * Indicates if a row is deleted. - */ - static final int ROW_ATTR_DELETED = 0XC0000000; // 3 - - /** - * The rowid mask. - */ - static final int ROW_ID_MASK = 0x3FFFFFFF; - - /** - * The row attributes mask. - */ - static final int ROW_ATTR_MASK = 0xC0000000; - - /** - * 'AND' of different result sets. - */ - static int WC_TYPE_AND_DIFF_RS = 0; - - /** - * 'OR' of different result sets. - */ - static int WC_TYPE_OR_DIFF_RS = 1; - - /** - * Used to count bits in an index bitmap. - */ - private static final byte[] bitsInNibble = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; - - /** - * Finds the next bit set from an b-tree. - * - * @param items The index bitmap. - * @param start The first value to search. - * @return The position of the next bit set. - */ - static int findNextBitSet(int[] items, int start) - { - int index = start >> 5, // Converts from bits to int. - n, - b; - - start &= 31; - while (true) - { - n = items.length - index; - if (n > 0 && items[index] == 0) - { - start = 0; - while (n > 0 && items[index] == 0) // Finds the next int with any bit set. - { - n--; - index++; - } - } - if (n > 0) // Found? - { - b = items[index]; - while (start < 32 && (b & (1 << start)) == 0) - start++; - if (start == 32) - { - start = 0; - index++; // No more bits in this int? Yests the next ints. - continue; - } - return start + (index << 5); - } - return -1; - } - } - - // juliana@253_5: removed .idr files from all indices and changed its format. - /** - * Compares 2 arrays of values. - * - * @param v1 The first array of values. - * @param v2 The second array of values. - * @param types The types of the values being compared. - * @param plainDB the plainDB of a table if it is necessary to load a string. - * @return 0 if the arrays are identical; a positive number if v1[] is greater than v2[]; otherwise, a negative number. - * @throws IOException If an internal method throws it. - */ - static int arrayValueCompareTo(SQLValue[] v1, SQLValue[] v2, byte[] types, PlainDB plainDB) throws IOException - { - int size = v1.length, - r, - i = -1; - while (++i < size) // juliana@210a_12: corrected wrong comparison order. - if ((r = v1[i].valueCompareTo(v2[i], types[i], false, false, plainDB)) != 0) - return r; - return 0; - } - - // juliana@253_5: removed .idr files from all indices and changed its format. - /** - * Compares the two records, using the sort column list. - * - * @param record1 The first record to be compared. - * @param record2 The second record to be compared. - * @param nullsRecord1 The null values of the first record. - * @param nullsRecord2 The null values of the second record. - * @param sortFieldList The order of evaluation of the record. - * @return 0 if the arrays are identical in the comparison order; a positive number if record1[] is greater than - * record2[]; otherwise, a negative number. - * @throws IOException If an internal method throws it. - */ - static int compareRecords(SQLValue[] record1, SQLValue[] record2, byte[] nullsRecord1, byte[] nullsRecord2, SQLResultSetField[] sortFieldList) throws IOException - { - - int i = -1, - n = sortFieldList.length, - result, - index; - SQLResultSetField field; - - while (++i < n) // Compares the records, using the sequence used by the sort column List. - { - field = sortFieldList[i]; - index = field.tableColIndex; - - // Compares the elements checking if they are null. - result = record1[index].valueCompareTo(record2[index], field.dataType, (nullsRecord1[index >> 3] & (1 << (index & 7))) != 0, - (nullsRecord2[index >> 3] & (1 << (index & 7))) != 0, null); - if (!field.isAscending) - result = -result; - if (result != 0) - return result; - } - return 0; - } - - // rnovais@101_1: this methods verifies the datapath - /** - * Gets the full name of a file: path + file name. - * - * @param fileName The file name. - * @param dataPath The path where the table is stored. - * @return path + file name if path is not empty, null or the root; otherwise, only the table name. - */ - static String getFullFileName(String fileName, String sourcePath) - { - if (sourcePath.length() > 0 && !sourcePath.equals("./")) // guich@102a_4: better logic, and fix. - return sourcePath.concat(fileName); - return fileName; - } - - /** - * Verifies if a string is a valid Time and transforms it into a correspondent int datetime. The time ranges from 00:00:00:000 to 23:59:59:9999 - * (it accepts dots and colons). This method is very flexible. For instance: 2:-:8:10 is the same as 2:0:8:10 and returns 20008010; - * 02:.:8:1 is the same as 02:0.0:8 and returns 20000008; :4:8:19 is the same as 0:4:8:19 and returns; 408019 2.4.a.876 is the same as 2.4.0.876 - * and returns 20400876. - * - * @param strTime A string in a time format. - * @return A correspondent int datetime. - * @throws SQLParseException If the value is not a valid time. - */ - static int testAndPrepareTime(String strTime) throws SQLParseException // rnovais@567_2 - { - int n = strTime.length(), - j = 2, - hh, - mm, - ss, - ms, - multiplier = 1; - int p[] = new int[3]; - char c; - - if (n > 0 && n <= 13) - { - int value = 0; - while (--n >= 0) // Going back to front and computing the value. - { - c = strTime.charAt(n); - if ('0' <= c && c <= '9') - { - value += (c - '0') * multiplier; - multiplier *= 10; - } - else if (j < 0) // juliana@270_20: solved a possible AIOOBE when passing an invalid time to DATETIME. - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_VALUE_ISNOT_DATETIME) + strTime); - else - { - p[j--] = value; - value = 0; - multiplier = 1; - } - } - hh = value; // There's at least one number. - mm = ++j < 3? p[j] : 0; - ss = ++j < 3? p[j] : 0; - ms = ++j < 3? p[j] : 0; - - if (!(hh > 23 || mm > 59 || ss > 59 || ms > 999)) - return hh * 10000000 + mm * 100000 + ss * 1000 + ms; - } - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_VALUE_ISNOT_DATETIME) + strTime); - } - - /** - * Formats an int "intTime" into a TIME hh:mm:ss:mmm and appends it to a StringBuffer. - * - * @param sBuffer The string buffer parameter. - * @param intTime An integer representing a time. - */ - static void formatTime(StringBuffer sBuffer, int intTime) - { - int mills = intTime % 1000; - int second = (intTime /= 1000) % 100; - int minute = (intTime /= 100) % 100; - int hour = (intTime / 100) % 100; - boolean useAmPm = !Settings.is24Hour; - int h; - - if (useAmPm) // guich@566_40 - if (hour == 0 || hour == 12) - h = 12; - else - h = hour < 12? hour : (hour - 12); - else - h = hour; - - sBuffer.append(h / 10).append(h % 10).append(Settings.timeSeparator).append(minute / 10).append(minute % 10).append(Settings.timeSeparator) - .append(second / 10).append(second % 10).append(Settings.timeSeparator).append(mills / 100).append(mills / 10 % 10).append(mills % 10); - - if (useAmPm) - sBuffer.append(' ').append(hour >= 12? 'P' : 'A').append('M'); - } - - /** - * Formats an pecific integer date as a string into a StringBuffer. - * - * @param sBuffer The string buffer parameter. - * @param intDate The date as an integer. - */ - static void formatDate(StringBuffer sBuffer, int intDate) - { - sBuffer.append(intDate / 10000000).append(intDate / 1000000 % 10).append(intDate / 100000 % 10).append(intDate / 10000 % 10) - .append('/').append(intDate / 1000 % 10).append(intDate / 100 % 10).append('/').append(intDate / 10 % 10).append(intDate % 10); - } - - /** - * Formats a date or a datetime as integers as a string using a StringBuffer. - * - * @param sBuffer The string buffer parameter. - * @param type The type of the value, DATE or DATETIME. - * @param sqlValue The record which stores the DATE, DATETIME, and the resulting string. - * @return The string representing the date or datetime. - */ - static String formatDateDateTime(StringBuffer sBuffer, int type, SQLValue sqlValue) - { - // juliana@252_1: corrected a bug with date and datetime using default. - - if (type == SQLElement.DATE) - { - sBuffer.setLength(0); - Utils.formatDate(sBuffer, sqlValue.asInt); - return sqlValue.asString = sBuffer.toString(); - } - if (type == SQLElement.DATETIME) - { - sBuffer.setLength(0); - Utils.formatDate(sBuffer, sqlValue.asInt); - sBuffer.append(' '); - Utils.formatTime(sBuffer, sqlValue.asShort); - return sqlValue.asString = sBuffer.toString(); - } - return sqlValue.asString; - } - - /** - * Verifies if the function can be applied to a data type field. - * - * @param parameterDataType The data type of the function parameter. - * @param sqlFunction The function code. - * @throws SQLParseException If the function can't be applied to the data type field. - */ - static void bindFunctionDataType(int parameterDataType, int sqlFunction) throws SQLParseException // rnovais@568_10 - { - byte functions[] = SQLElement.function_x_datatype[parameterDataType]; - int j = functions.length; - while (--j >= 0) - if (functions[j] == sqlFunction) - return; - throw new SQLParseException(LitebaseMessage.getMessage(LitebaseMessage.ERR_INCOMPATIBLE_TYPES) - + " " + SQLElement.dataTypeFunctionsNames[sqlFunction]); - } - - /** - * Sets and resets one bit in an array of bytes. - * @param items The array of bytes. - * @param index The bit index to be set or reseted. - * @param on A boolean that defines whether the bit will be set or reseted. - */ - static void setBit(byte[] items, int index, boolean on) - { - if (on) - items[index >> 3] |= (1 << (index & 7)); // set - else - items[index >> 3] &= ~(1 << (index & 7)); // reset - } - - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. - /** - * Converts the a string into a float. - * - * @param string The string to be converted. - * @return the float represented by the string. - * @throws InvalidNumberException If the string passed is not a valid float. - */ - static float toFloat(String string) throws InvalidNumberException - { - try - { - return Float.valueOf(string).floatValue(); - } - catch (NumberFormatException exception) - { - throw new InvalidNumberException("Error: " + string + " is not a valid float value."); - } - } - - // juliana@230_12: improved recover table to take .dbo data into consideration. - /** - * Transforms a string into an unicode byte array. - * - * @param string The string to be transformed. - * @return The array representing the string bytes. - */ - static byte[] toByteArray(String string) - { - int length = string.length(); - byte[] byteArray = new byte[length << 1]; - char current; - - while (--length >= 0) - { - byteArray[length << 1] = (byte)(current = string.charAt(length)); - byteArray[(length << 1) + 1] = (byte)(current >>= 8); - } - return byteArray; - } - - /** - * Calculates the hash code of a substring of a string. - * - * @param string The string. - * @param initialIdx The initial index of the string to calculate the hash code. - * @return The hash code of the substring. - */ - static int subStringHashCode(String string, int initialIdx) - { - int hashCode = 0, - length = string.length() - initialIdx; - - while (--length >= 0) - hashCode = (hashCode << 5) - hashCode + (int)string.charAt(initialIdx++); - return hashCode; - } - - /** - * Counts the number of ON bits. - * - * @param elems The array where the bits will be counted. - * @return The number of on bits. - */ - static int countBits(int[] elems) - { - if (elems == null) - return 0; - int c = 0, - i = elems.length, - j, - v; - while (--i >= 0) - { - v = elems[i]; - j = 8; - while (--j >= 0) - { - c += bitsInNibble[v & 0xF]; - v >>= 4; - } - } - return c; - } -} diff --git a/LitebaseSDK/src/java/litebase/XFile.java b/LitebaseSDK/src/java/litebase/XFile.java deleted file mode 100644 index 1973f31f56..0000000000 --- a/LitebaseSDK/src/java/litebase/XFile.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase; - -import totalcross.io.*; - -/** - * This is a cross-file interface. It defines operations common to NormalFile and MemoryFile. - */ -@Deprecated -abstract class XFile extends Stream -{ - /** - * The file size. - */ - int size; - - /** - * The current position in the file. - */ - int pos; - - /** - * The final position of the file. - */ - int finalPos; - - /** - * Sets the file pointer to the given position. If it is beyond the end of the file, an exception will be thrown. - * - * @param position The desired position of the file. - */ - abstract void setPos(int position) throws IOException; - - /** - * Enlarges the file. - * - * @param newSize The new file size. - */ - abstract void growTo(int newSize) throws IOException; - - /** - * Does nothing. This method is here because it extends Stream. - * - * @throws IOException If an internal method throws it. - */ - public void close() throws IOException {} - - /** - * Does nothing. This method is here because it extends Stream. - * - * @param newName Ignored. - */ - void rename(String newName) throws IOException {} -} diff --git a/LitebaseSDK/src/java/litebase/android/Loader.java b/LitebaseSDK/src/java/litebase/android/Loader.java deleted file mode 100644 index 885a2c739c..0000000000 --- a/LitebaseSDK/src/java/litebase/android/Loader.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase.android; - -import android.app.*; -import android.os.*; -import android.util.*; -import totalcross.AndroidUtils; - -public class Loader extends Activity -{ - public Handler achandler; - - /** Called when the activity is first created. */ - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - try - { - AndroidUtils.initialize(this); - AndroidUtils.checkInstall(getApplicationContext()); - setResult(RESULT_OK); - finish(); - } - catch (Exception e) - { - String stack = Log.getStackTraceString(e); - AndroidUtils.debug(stack); - AndroidUtils.error("An exception was issued when launching Litebase. Please inform this stack trace to your software's vendor:\n\n"+stack,true); - setResult(RESULT_CANCELED); - } - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/litebase/package.html b/LitebaseSDK/src/java/litebase/package.html deleted file mode 100644 index 74e785c920..0000000000 --- a/LitebaseSDK/src/java/litebase/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - -Package that contains the classes that manipulate files using SQL. -

    -You must install the files LitebaseLib.pdb (for Palm OS) or LitebaseLib.tcz (for all other platforms), -and Litebase.prc (for Palm OS) or Litebase.dll (for all other platforms). - - \ No newline at end of file diff --git a/LitebaseSDK/src/java/litebase/ui/DBListBox.java b/LitebaseSDK/src/java/litebase/ui/DBListBox.java deleted file mode 100644 index 22b6d996c4..0000000000 --- a/LitebaseSDK/src/java/litebase/ui/DBListBox.java +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package litebase.ui; -import totalcross.ui.gfx.*; -import totalcross.ui.*; - -/** - * DBListBox is a simple implementation of a Listbox. - * You can use the up/down keys to scroll and enter the first - * letter of an item to select it. - *

    - * Note: the color used in the setBackground method will be used in the scrollbar - * only. The background color of the control will be a lighter version of the - * given color. - *

    - * Note: this is a special version for the LitebaseConnection. It accepts - * a String matrix as the input, and does not allow add/remove/set of elements, - * since it reflects the database and must be filled again after some row is - * updated. - *

    - * Here is an example showing how it can be used: - * - *

    - * import litebase.ui.*;
    - * import litebase.*;
    - *
    - * public class MyProgram extends totalcross.ui.MainWindow
    - * {
    - * DBListBox lb;
    - *
    - * public void initUI()
    - * {
    - *   ResultSet rs = driver.executeQuery("select rowid,name from person where name > 20");
    - *   rs.first();
    - *   lb = new DBListBox(rs.getStrings(-1,true,false),1); // display column 1
    - *   add(lb,LEFT,TOP);
    - * }
    - *
    - * public void onEvent(totalcross.ui.event.Event event)
    - * {
    - *    switch (event.type)
    - *    {
    - *       case totalcross.ui.event.ControlEvent.PRESSED:
    - *          if (event.target == lb)
    - *             String[] element = (String[])lb.getSelectedItem(); // in most cases, this is just a String and may be casted to such
    - *    }
    - * }
    - * }
    - * 
    - * The first item has index 0. - */ - -public class DBListBox extends ListBox -{ - /** The String items */ - protected String [][]sitems; - - /** Creates an empty Listbox. */ - public DBListBox() - { - this(null,-1); - } - - /** Creates a Listbox with the given items. */ - public DBListBox(String[][] items, int displayCol) - { - super((Object[])null); - if (items != null) - { - sitems = items; - itemCount = items.length; - } - this.dataCol = displayCol; - } - - /** Sets the column of the String matrix that will be used to display the - * elements. - */ - public void setDisplayCol(int displayCol) - { - this.dataCol = displayCol; - } - - /** Returns the number of columns, if there are items. - */ - public int getColumnCount() - { - return sitems == null || sitems.length == 0 ? 1 : sitems[0].length; - } - - /** REPLACES the current items with the given ones. */ - public void add(Object []items) - { - if (items instanceof String[][]) - { - sitems = (String[][])items; - itemCount = sitems.length; - sbar.setEnabled(isEnabled() && visibleItems < itemCount); - sbar.setMaximum(itemCount); // guich@210_12: forgot this line! - } - } - - /** REPLACES the current items with the given ones. */ - public void add(Object item) - { - if (item instanceof String[][]) - add((Object[])item); - } - - /** Does nothing */ - public void insert(Object item, int index) - { - } - - /** Empties this ListBox, setting all elements of the array to null - so they can be garbage collected. - Attention! If you used the same object array - to initialize two ListBoxes (or ComboBoxes), this method will null both ListBoxes - ('cos they use the same array reference), - and you'll get a null pointer exception! - */ - public void removeAll() // guich@210_13 - { - sitems = null; - sbar.setMaximum(0); - itemCount = 0; - offset=0; // wolfgang@330_23 - repaint(); - } - - /** Does nothing */ - public void remove(int itemIndex) // guich@200final_12: new method - { - } - - /** Does nothing */ - public void remove(Object item) - { - } - - /** Does nothing */ - public void setItemAt(int i, Object s) - { - } - - /** Get the Object at the given Index. Returns a String array, or null if i is out of range. */ - public Object getItemAt(int i) - { - if (0 <= i && i < itemCount) - return sitems[i]; - return null; - } - - /** Returns the selected item of the Listbox or null if none is selected */ - public Object getSelectedItem() - { - return selectedIndex >= 0 ? sitems[selectedIndex] : null; - } - - /** Returns the position of the selected item of the Listbox or -1 if the listbox has no selected index yet. */ - public int getSelectedIndex() - { - return selectedIndex; - } - - /** Returns all items in this ListBox. The array can be casted to String[][]. - */ - public Object []getItems() - { - return sitems; - } - - protected Object []getItemsArray() - { - return sitems; - } - - /** Returns the index of the object at the given column. */ - public int indexOf(Object name, int col) - { - for (int i = 0; i < sitems.length; i++) - if (sitems[i][col].equals(name)) - return i; - return -1; - } - - /** Select an item and scroll to it if necessary. Note: select must be called only after the control has been added to the container and its rect has been set. */ - public void setSelectedIndex(int i) - { - if (0 <= i && i < itemCount && i != selectedIndex/* && height != 0*/) // flsobral@220_14: commented height!=0 otherwise combobox are not properly set. (same problem observed with listbox) - { - offset=i; - int vi = sbar.getVisibleItems(); - int ma = sbar.getMaximum(); - if (offset+vi > ma) // astein@200b4_195: fix list items from being lost when the comboBox.setSelectedIndex() method is used - offset=Math.max(ma-vi,0); // guich@220_4: fixed bug when the listbox is greater than the current item count - - selectedIndex = i; - sbar.setValue(offset); // guich@210_9: fixed scrollbar update when selecting items - repaint(); - } - else - if (i == -1) // guich@200b4_191: unselect all items - { - offset = 0; - sbar.setValue(0); - selectedIndex = -1; - repaint(); - } - } - - /** Returns the number of items */ - public int size() - { - return itemCount; - } - - /** Does nothing */ - public void add(Control control) - { - } - /** Does nothing */ - public void remove(Control control) - { - } - - /** Does nothing */ - protected void find(char c) - { - } - - /** You can extend ListBox and overide this method to draw the items */ - protected void drawItem(Graphics g, int index, int dx, int dy) - { - //Vm. debug(this+" index: "+index+", items.size: "+items.size()+", dx,dy = "+dx+","+dy); - if (0 <= index && index < itemCount) - g.drawText(sitems[index][dataCol],dx,dy); - } - - /** Returns the width of the given item index with the current fontmetrics. Note: if you overide this class you must implement this method. */ - protected int getItemWidth(int index) - { - if (sitems == null) return 0; - return fm.stringWidth(sitems[index][dataCol]); - } - - private void qsort(int first, int last) // guich@220_34 - { - String[][] sitems = this.sitems; - int low = first; - int high = last; - if (first >= last) - return; - String mid = sitems[(first+last) >> 1][dataCol]; - while (true) - { - while (high >= low && mid.compareTo(sitems[low][dataCol]) > 0) // guich@_566_25: added "high > low" here and below - guich@_568_5: changed to >= - low++; - while (high >= low && mid.compareTo(sitems[high][dataCol]) < 0) - high--; - if (low <= high) - { - String []temp = sitems[low]; - sitems[low++] = sitems[high]; - sitems[high--] = temp; - } - else break; - } - if (first < high) - qsort(first,high); - if (low < last) - qsort(low,last); - } - - /** Sorts the elements of this ListBox. The current selection is cleared. */ - public void qsort() - { - qsort(0,itemCount-1); - setSelectedIndex(-1); - } - - public String getText() - { - return selectedIndex < 0 ? "" : sitems[selectedIndex][dataCol]; - } - -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/litebase/ui/package.html b/LitebaseSDK/src/java/litebase/ui/package.html deleted file mode 100644 index fc338f2840..0000000000 --- a/LitebaseSDK/src/java/litebase/ui/package.html +++ /dev/null @@ -1,5 +0,0 @@ - - -Useful controls to be used with Litebase. - - \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/addressbook/AddressBook.java b/LitebaseSDK/src/java/samples/apps/addressbook/AddressBook.java deleted file mode 100644 index d8aab37651..0000000000 --- a/LitebaseSDK/src/java/samples/apps/addressbook/AddressBook.java +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.addressbook; - -import litebase.*; -import totalcross.sys.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.util.*; - -/** - * A simple address book application that demonstrates a real case where Litebase can be used. It also shows how to use the grid. - */ -public class AddressBook extends MainWindow implements Grid.DataSource -{ - /** - * The connection with Litebase. - */ - LitebaseConnection driver; - - /** - * The tabbed container for changing windows. - */ - private TabbedContainer container; - - /** - * The grid for the table contents. - */ - private Grid grid; - - /** - * The object to edit people data. - */ - private Edition edit; - - /** - * The current result set used. - */ - ResultSet activeRS; - - /** - * A prepared statement to list all the table elements. - */ - PreparedStatement psList; - - static - { - Settings.useNewFont = true; - } - - /** - * The constructor. - */ - public AddressBook() - { - super("TC Address Book", TAB_ONLY_BORDER); - if (Settings.onJavaSE) - totalcross.sys.Settings.showDesktopMessages = false; - setUIStyle(Settings.Android); - driver = LitebaseConnection.getInstance("ABok"); - } - - /** - * Initializes the user interface. - */ - public void initUI() - { - createTables(); // Data base setup stage. - - // User interface set up stage. - String[] tpCaptions = {"Listing", "Edition"}; - add(container = new TabbedContainer(tpCaptions)); - container.setBorderStyle(Window.NO_BORDER); - container.setRect(getClientRect()); - edit = new Edition(); - edit.book = this; - container.setContainer(1, edit); - - String[] gridCaptions = {"id", "Name", "Address", "Phone", "Birthday", "Salary", "Married", "Gender", "Last Updated"}; - int gridWidths[] = - { - 0, // id - fm.stringWidth("aaaaaaaaaa"), // name - fm.stringWidth("aaaaaaaaaaaaaaa"), // address - fm.stringWidth(edit.edPhone.getMask()), // phone - fm.stringWidth(edit.edBirth.getMask()), // birthday - fm.stringWidth(edit.edSalary.getMask()), // salary - 1, 1, // The title width will be replaced by the value because it is bigger. - fm.stringWidth("99/99/9999 99:99:99") - }; - - int gridAligns[] = {LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT}; - container.setContainer(0, grid = new Grid(gridCaptions, gridWidths, gridAligns, false)); - - invalidateRS(); - } - - /** - * Called to process posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - switch (event.type) - { - case ControlEvent.PRESSED: - if (event.target == container) - switch (container.getActiveTab()) - { - case 0: // listing - invalidateRS(); // Update the grid. - break; - case 1: // edition - String[] item = grid.getSelectedItem(); - if (item == null) - edit.clear(); - else - { - try - { - edit.rowId = Convert.toInt(item[0]); - } - catch (InvalidNumberException exception) {} - edit.show(); - } - } - } - } - - /** - * Gets some results if there is an opened result set. - * - * @param startIndex The first record shown. - * @param count The number of records to be shown. - * @return A string matrix with all the desired results. - */ - public String[][] getItems(int startIndex, int count) - { - if (activeRS != null) - { - activeRS.absolute(startIndex); - - String[][] matrix = activeRS.getStrings(count); - int size = matrix.length, - date, - time; - long dateTime; - Date tempDate = edit.tempDate; - Time tempTime = edit.tempTime; - StringBuffer sb = edit.buffer; - - try - { - while (--size >= 0) - { - date = Convert.toInt(matrix[size][4]); - tempDate.set(date % 100, (date /= 100) % 100, date / 100); - matrix[size][4] = edit.tempDate.toString(); - matrix[size][6] = matrix[size][6].equals("1")? "Y" : "N"; - matrix[size][7] = matrix[size][7].equals("1")? "M" : "F"; - dateTime = Convert.toLong(matrix[size][8]); - time = (int)(dateTime % 1000000); - date = (int)(dateTime / 1000000); - tempDate.set(date % 100, (date /= 100) % 100, date / 100); - tempTime.second = time % 100; - tempTime.minute = (time /= 100) % 100; - tempTime.hour = time / 100; - sb.setLength(0); - matrix[size][8] = sb.append(tempDate).append(' ').append(tempTime).toString(); - } - } - catch (InvalidNumberException exception) {} - catch (InvalidDateException exception) {} - return matrix; - } - return null; - } - - /** - * Invalidates the result set to get a new one. - */ - void invalidateRS() - { - if (activeRS != null) - activeRS.close(); - - if (psList != null) - { - (activeRS = psList.executeQuery()).setDecimalPlaces(6, 2); - if (activeRS.first()) // Maybe the result set is empty (nothing in table). - grid.setDataSource(this, activeRS.getRowCount()); - else - { - grid.removeAllElements(); - activeRS.close(); - activeRS = null; - } - } - } - - /** - * Creates the table. - */ - private void createTables() - { - try - { - // Uses the rowid as the person id. - if (!driver.exists("bookentry")) - { - driver.execute("create table bookentry(name char(30), address char(50), phone char(20), birthday int, salary float, married short, " - + "gender short, lastUpdated long, photoidx short)"); - driver.execute("CREATE INDEX IDX_0 ON bookentry(rowid)"); // The index names are completely ignored. - } - - // Photo table for the data base. - if (!driver.exists("photodb")) - driver.execute("create table photodb(photo blob(16384))"); - - PreparedStatement ps = driver.prepareStatement("insert into photodb values (?)"); - ps.setBlob(0, Vm.getFile("fabio.jpg")); - ps.executeUpdate(); - ps.setBlob(0, Vm.getFile("guilherme.jpg")); - ps.executeUpdate(); - ps.setBlob(0, Vm.getFile("juliana.jpg")); - ps.executeUpdate(); - } - catch (AlreadyCreatedException exception) {} // ignored - - psList = driver.prepareStatement("select rowid, name, address, phone, birthday, salary, married, gender, lastUpdated, photoidx from " - + "bookentry"); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/addressbook/Edition.java b/LitebaseSDK/src/java/samples/apps/addressbook/Edition.java deleted file mode 100644 index 24d5a0daf9..0000000000 --- a/LitebaseSDK/src/java/samples/apps/addressbook/Edition.java +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.addressbook; - -import litebase.*; -import totalcross.sys.*; -import totalcross.ui.*; -import totalcross.ui.dialog.MessageBox; -import totalcross.ui.event.*; -import totalcross.ui.gfx.Color; -import totalcross.ui.image.*; -import totalcross.ui.media.Sound; -import totalcross.util.*; - -class Edition extends Container -{ - /** - * The edit for entering the person name. - */ - private Edit edName; - - /** - * The edit for entering the person address. - */ - private Edit edAddress; - - /** - * The edit for entering the person phone. - */ - Edit edPhone; - - /** - * The edit for entering the person birthday. - */ - Edit edBirth; - - /** - * The edit for entering the person salary. - */ - Edit edSalary; - - /** - * The radio button for male gender. - */ - private Radio rdMale; - - /** - * The radio button for female gender. - */ - private Radio rdFemale; - - /** - * The group for the gender radio boxes. - */ - private RadioGroupController rgGender; - - /** - * The check box for the civil status. - */ - private Check chMarried; - - /** - * The push button group for editing a person information. - */ - private PushButtonGroup pbgAction; - - /** - * The OK button, which executes the selected operation. - */ - private Button btnOk; - - /** - * Next button. - */ - private Button btnNext; - - /** - * Previous button. - */ - private Button btnPrev; - - /** - * The status label which shows that last time when the table was updated. - */ - private Label lbStatus; - - /** - * The image controler. - */ - private ImageControl imgCtrl; - - /** - * The address book object. - */ - AddressBook book; - - /** - * The rowid of the selected row. - */ - int rowId; - - /** - * The photo rowid. - */ - private int photoIdx; - - /** - * The connection with Litebase; - */ - private LitebaseConnection driver; - - /** - * A prepared statement for deleting from the table. - */ - private PreparedStatement psDelete; - - /** - * A prepared statement for inserting into the table. - */ - private PreparedStatement psInsert; - - /** - * A prepared statement for updating from the table. - */ - private PreparedStatement psUpdate; - - /** - * A prepared statement for selecting a row from the table. - */ - private PreparedStatement psSelect; - - /** - * A result set with the pictures. - */ - private ResultSet photoResultSet; - - /** - * A temporary date object to save memory. - */ - Date tempDate = new Date(); - - /** - * A temporary time object to save memory. - */ - Time tempTime = new Time(); - - /** - * A temporary buffer to save memory. - */ - StringBuffer buffer = new StringBuffer(100); - - /** - * Initializes the user interface. - */ - public void initUI() - { - Label labelAux = new Label("Birthday"); - Edit editAux = edBirth = new Edit("99/99/9999") ; - int xx = labelAux.getPreferredWidth() + 5; // Computes the max width so that all can stay at the same position. - - // name - add(new Label("Name"),LEFT + 1, AFTER + 2); - add(edName = new Edit(""), xx, SAME - 1); - edName.setMaxLength(30); - - // address - add(new Label("Address"),LEFT + 1,AFTER + 1); - add(edAddress = new Edit(""), xx, SAME - 1); - edAddress.setMaxLength(50); - - // phone - add(new Label("Phone"),LEFT + 1,AFTER + 1); - add(edPhone = new Edit("+99 99 9999-9999"), xx, SAME - 1); - edPhone.setMaxLength(20); - - // birthday - add(labelAux, LEFT + 1, AFTER + 1); - add(editAux = edBirth = new Edit("99/99/9999"), xx, SAME - 1); - editAux.setMode(Edit.DATE); - editAux.setMaxLength(10); - - // salary - add(new Label("Salary"), LEFT, AFTER + 1); - add(edSalary = new Edit("9999999.99"), xx, SAME - 1); - edSalary.setMode(Edit.CURRENCY); - - // gender - add(new Label("Gender"), LEFT, AFTER + 1); - add(rdMale = new Radio("Male", rgGender = new RadioGroupController()), xx, SAME - 1); - add(rdFemale = new Radio("Female", rgGender), AFTER + 2, SAME); - rdMale.setChecked(true); - - // Civil status. - add(chMarried = new Check("Married"), xx, AFTER + 1); - - // action - add(pbgAction = new PushButtonGroup(new String[]{"Insert", "Update", "Delete", "Clear"}, false, -1, 0, 4, 0, false, PushButtonGroup.NORMAL), - LEFT, AFTER + 2); - add(btnOk = new Button(" Ok "), RIGHT, SAME); - - // status - (labelAux = lbStatus = new Label("", CENTER)).setInvert(true); - labelAux.setForeColor(Color.brighter(getForeColor())); - add(labelAux, LEFT, BOTTOM); - - // picture - add(new Spacer(" "), CENTER, BEFORE - 10, PREFERRED, PREFERRED, labelAux); - add(btnPrev = new Button(" << "), BEFORE, SAME); - add(btnNext = new Button(" >> "), AFTER, SAME); - add(imgCtrl = new ImageControl(), LEFT, AFTER + 10, FILL, FIT, btnOk); - imgCtrl.setEventsEnabled(true); - - driver = book.driver; - psDelete = driver.prepareStatement("delete bookentry where rowid = ?"); - psInsert = driver.prepareStatement("insert into bookentry values (?, ?, ?, ?, ?, ?, ?, ?, ?)"); - psUpdate = driver.prepareStatement("update bookentry set name = ?, address = ?, phone = ?, birthday = ?, salary = ?, married = ?, gender = ?," - + "lastUpdated = ?, photoidx = ? where rowid = ?"); - psSelect = driver.prepareStatement("select * from bookentry where rowid = ?"); - photoResultSet = driver.executeQuery("select rowid, photo from photodb"); - - setPicture(0); - } - - /** - * Verifies if all the fields are ok. - * - * @return true if all the field values are valid; false, otherwise. - */ - private boolean verifyFields() - { - StringBuffer sb = buffer; - - sb.setLength(0); - if (edName.getText().length() == 0) - sb.append("name |"); - if (edAddress.getText().length() == 0) - sb.append("address |"); - if (edPhone.getText().length() == 0) - sb.append("phone |"); - try - { - if (Convert.toDouble(edSalary.getText()) < 0) - sb.append("salary |"); - } catch (InvalidNumberException exception) - { - sb.append("salary |"); - } - try - { - tempDate.set(edBirth.getText(), Settings.dateFormat); - } catch (InvalidDateException exception) - { - sb.append("birthday |"); - } - - if (sb.length() > 0) // Any fields with problems? - { - sb.setLength(sb.length() -1); // Removes the last |. - new MessageBox("Attention","You must fill/correct | the following fields: |" + sb).popupNonBlocking(); - return false; - } - return true; - } - - /** - * Called to process posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnOk) - { - switch (pbgAction.getSelectedIndex()) - { - case 0: // insert - if (verifyFields()) - doInsertUpdate(true); - break; - case 1: // update - if (verifyFields()) // Only updates if the row exists. - if (rowId > 0) - doInsertUpdate(false); - else - Sound.beep(); - break; - case 2: // delete - if (rowId > 0) // Only deletes if the row exists. - doDelete(); - else - Sound.beep(); - break; - case 3: // clear - clear(); - break; - default: - Sound.beep(); - } - } - else if (event.target == btnPrev) // Button prev: goes to the previous picture. - move(-1); - else if (event.target == btnNext) // Button next: goes to the next picture. - move(1); - } - } - - /** - * Executes the selected operation in the database. - * - * @param isInsert Indicates if the operation is an insert or update. - */ - private void doInsertUpdate(boolean isInsert) - { - String name = edName.getText(), - addr = edAddress.getText(), - phone = edPhone.getText(); - int birth = -1, - married = chMarried.isChecked()? 1 : 0, - gender = rdMale.isChecked()? 1 : 0, - rows = -1; - double salary = 0; - long lastUpdated = new Time().getTimeLong(); - PreparedStatement psAux; - - try - { - birth = tempDate.set(edBirth.getText(), Settings.dateFormat); - salary = Convert.toDouble(edSalary.getText()); - } - catch (Exception exception) - { - MessageBox.showException(exception, true); - } - - if (isInsert) - psAux = psInsert; - else - (psAux = psUpdate).setInt(9, rowId); - - psAux.setString(0, name); - psAux.setString(1, addr); - psAux.setString(2, phone); - psAux.setInt(3, birth); - psAux.setFloat(4, salary); - psAux.setShort(5, (short)married); - psAux.setShort(6, (short)gender); - psAux.setLong(7, lastUpdated); - psAux.setShort(8, (short)photoIdx); - rows = psAux.executeUpdate(); - if (rows == 1) - { - book.invalidateRS(); - clear(); - } - else - Sound.beep(); - } - - /** - * Deletes a row from the table. - */ - private void doDelete() - { - psDelete.setInt(0, rowId); - if (psDelete.executeUpdate() == 1) - { - book.invalidateRS(); - clear(); - } - else - Sound.beep(); - } - - /** - * Clears all the fields. - */ - public void clear() - { - edName.setText(""); - edAddress.setText(""); - edPhone.setText(""); - edBirth.setText(""); - edSalary.setText(""); - chMarried.setChecked(false); - rgGender.setSelectedItem(rdMale); - pbgAction.setSelectedIndex(-1); - lbStatus.setText(""); - rowId = -1; - setPicture(0); - } - - /** - * Shows the selected row for edition. - */ - public void show() - { - psSelect.setInt(0, rowId); - ResultSet resultSet = psSelect.executeQuery(); - resultSet.next(); - edName.setText(resultSet.getString(1)); - edAddress.setText(resultSet.getString(2)); - edPhone.setText(resultSet.getString(3)); - - int date = resultSet.getInt(4); - try - { - tempDate.set(date % 100, (date /= 100) % 100, date / 100); - edBirth.setText("" + tempDate); - } - catch (InvalidDateException exception) {} - - edSalary.setText(Convert.toString(resultSet.getFloat(5), 2)); - - chMarried.setChecked(resultSet.getString(6).charAt(0) == '1'); - - String gender = resultSet.getString(7); - rgGender.setSelectedItem(gender.charAt(0) == '1'? rdMale : rdFemale); // Activates the proper one. - - long dateTime = resultSet.getLong(8); - int time = (int)(dateTime % 1000000); - date = (int)(dateTime / 1000000); - - try - { - tempDate.set(date % 100, (date /= 100) % 100, date / 100); - Time timeAux = tempTime; - timeAux.second = time % 100; - timeAux.minute = (time /= 100) % 100; - timeAux.hour = time / 100; - StringBuffer sb = buffer; - sb.setLength(0); - lbStatus.setText(sb.append("Last updated: ").append(tempDate).append(' ').append(timeAux).toString()); - } catch (InvalidDateException exception) {} - - pbgAction.setSelectedIndex(1); - setPicture(resultSet.getShort(9)); - } - - /** - * Moves the current record by a step and load a person image. - * - * @param step The sted - */ - private void move(int step) - { - int length = 3; - // Selects the current index. - - photoIdx = (photoIdx + step) % length; - if (photoIdx < 0) - photoIdx += length; - - setPicture(photoIdx); // Selects the person image. - } - - /** - * Sets the right picture in its control. - * @param photoIdx The picture index. - */ - private void setPicture(int newPhotoIdx) - { - photoResultSet.absolute(photoIdx = newPhotoIdx); - try - { - imgCtrl.setImage(new Image(photoResultSet.getBlob(2))); - } - catch (ImageException exception) - { - MessageBox.showException(exception, true); - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/logger/PlayLog.java b/LitebaseSDK/src/java/samples/apps/logger/PlayLog.java deleted file mode 100644 index a18e1c2883..0000000000 --- a/LitebaseSDK/src/java/samples/apps/logger/PlayLog.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.logger; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.ui.*; -import totalcross.ui.dialog.*; -import totalcross.ui.event.*; -import totalcross.ui.gfx.Color; -import litebase.*; - -// juliana@250_9: added a new sample: LoggerSample. This is to execute a Litebase log file and try to reproduce a problem. -/** - * A sample to help in reproducing errors by executing a logger file. - */ -public class PlayLog extends MainWindow -{ - /** - * The connection with Litebase. - */ - private LitebaseConnection driver; - - /** - * The button to choose the file. - */ - private Button button; - - /** - * The default path. - */ - private String dataPath = (Settings.dataPath != null && Settings.dataPath.length() != 0? Settings.dataPath : Settings.appPath); - - static - { - Settings.useNewFont = true; - } - - /** - * The constructor. - */ - public PlayLog() - { - super("Play Log", TAB_BORDER); - setUIStyle(Settings.Android); - } - - /** - * Initializes the user interface. - */ - public void initUI() - { - (button = new Button(" Choose file ")).setBackForeColors(Color.WHITE, Color.BLACK); - add(new Label("Load logger file:"), LEFT + 5, TOP + 5); - add(button, AFTER + 5, SAME); - } - - /** - * Called to process the event when the button is pressed. - * - * @param event The event to be processed. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED && event.target == button) - { - try - { - // Opens a file chooser box for choosing a Litebase log file. - FileChooserBox fcb = new FileChooserBox(new FileChooserBox.Filter() - { - // Shows only Litebase log files. - public boolean accept(File file) throws IOException - { - String path = file.getPath(); - return file.isDir() || (path.indexOf("LITEBASE_") >= 0 && path.endsWith(".LOGS")); - } - }); - - fcb.mountTree("device/"); - fcb.popup(); - - // Only process a logger file if there is a chosen file to be processed. - String loggerName = fcb.getAnswer(); - if (loggerName != null && loggerName.length() > 0) - { - LineReader lineReader = new LineReader(new File(loggerName, File.READ_ONLY)); // Opens the log file. - String string; - int i; - - try // Erases the test database if it exists. - { - LitebaseConnection.dropDatabase("Test", dataPath, 1); // Erases the previous tables. - } - catch (DriverException exception) {} - - // Tells the user that the logger is being executed. - MessageBox pleaseWait = new MessageBox("Please Wait", "Processing log file...", null); - pleaseWait.popupNonBlocking(); - - while ((string = lineReader.readLine()) != null) // Executes the possible log sql commands. Do not simulate prepared statements. - { - // Transforms the string into lower case, trims it and removes possible indications that it is a prepared statement. - if ((string = string.toLowerCase().trim()).startsWith("prep: ")) - string = string.substring(6); - - // Changes a blob indication with null. This may difficult finding problems with blobs. - string = Convert.replace(string, "[blob]", "null"); - - try - { - if (string.startsWith("select")) - driver.executeQuery(string); - else if (string.startsWith("update") || string.startsWith("insert") - || string.startsWith("delete") ||string.startsWith("drop") || string.startsWith("alter")) - driver.executeUpdate(string); - else if (string.startsWith("create")) - driver.execute(string); - else if (string.startsWith("preparestatement")) - driver.prepareStatement(string.substring(17)); - else if (string.startsWith("new")) - driver = LitebaseConnection.getInstance("Test"); - else if (string.startsWith("close")) - driver.closeAll(); - else if (string.startsWith("purge")) - driver.purge(string.substring(6)); - else if (string.startsWith("getrowcountdeleted")) - driver.getRowCountDeleted(string.substring(19)); - else if (string.startsWith("getrowcount")) - driver.getRowCount(string.substring(12)); - else if (string.startsWith("getcurrentrowid")) - driver.getCurrentRowId(string.substring(16)); - else if (string.startsWith("getrowiterator")) - driver.getRowIterator(string.substring(15)); - else if (string.startsWith("convert")) - driver.convert(string.substring(8)); - else if (string.startsWith("recover table")) - driver.convert(string.substring(14)); - else if (string.startsWith("setrowinc")) - { - string = string.substring(10); - i = string.indexOf(' '); - driver.setRowInc(string.substring(0, i), Convert.toInt(string.substring(i + 1, string.length()))); - } - else if (string.length() > 0) - Vm.debug("not executed - " + string); - } - catch (RuntimeException exception) - { - Vm.debug(string); - Vm.debug(exception.toString()); - } - } - - // Tells the user that the processing has finished. - pleaseWait.unpop(); - new MessageBox("Finished", "Finished processing").popup(); - } - } - catch (IOException exception) - { - MessageBox.showException(exception, true); - } - catch (InvalidNumberException exception) - { - MessageBox.showException(exception, true); - } - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/SalesPlus.java b/LitebaseSDK/src/java/samples/apps/salesplus/SalesPlus.java deleted file mode 100644 index b90e5f564f..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/SalesPlus.java +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus; - -import litebase.LitebaseConnection; -import samples.apps.salesplus.ui.BaseMenu; -import samples.apps.salesplus.ui.MainMenu; -import samples.apps.salesplus.ui.customer.*; -import samples.apps.salesplus.ui.order.*; -import samples.apps.salesplus.ui.product.*; -import samples.apps.salesplus.ui.report.ReportMenu; -import samples.apps.salesplus.ui.report.byproduct.*; -import samples.apps.salesplus.ui.report.summary.*; -import totalcross.io.IOException; -import totalcross.sys.Settings; -import totalcross.ui.*; -import totalcross.ui.dialog.*; -import totalcross.ui.gfx.*; -import totalcross.ui.image.*; -import totalcross.util.Date; - -public class SalesPlus extends MainWindow -{ - /** - * The default background color. - */ - public static final int DEFAULT_BACK_COLOR = 0xd9d9ff; - - /** - * The default foreground color. - */ - public static final int DEFAULT_FORE_COLOR = 0x000099; - - // Constants for swapping between screens. Each one assigns a window to be swapped to. - /** - * The main menu window. - */ - public static final int MAIN_MENU = 0; - - /** - * The costumer menu window. - */ - public static final int CUSTOMER_MENU = 1; - - /** - * The order menu window. - */ - public static final int ORDER_MENU = 2; - - /** - * The product menu window. - */ - public static final int PRODUCT_MENU = 3; - - /** - * The report menu window. - */ - public static final int REPORT_MENU = 4; - - /** - * The summary menu window. - */ - public static final int SUMMARY_MENU = 5; - - /** - * The by product menu window. - */ - public static final int BY_PRODUCT_MENU = 6; - - /** - * The new product window. - */ - public static final int NEW_PRODUCT = 7; - - /** - * The product search window. - */ - public static final int PRODUCT_SEARCH = 8; - - /** - * The new customer window. - */ - public static final int NEW_CUSTOMER = 9; - - /** - * The customer search window. - */ - public static final int CUSTOMER_SEARCH = 10; - - /** - * The new order window. - */ - public static final int NEW_ORDER = 11; - - /** - * The order search window. - */ - public static final int ORDER_SEARCH = 12; - - /** - * The summary report day window. - */ - public static final int DAY = 13; - - /** - * The summary report month window. - */ - public static final int MONTH = 14; - - /** - * The by product day window. - */ - public static final int DAY2 = 15; - - /** - * The by product month window. - */ - public static final int MONTH2 = 16; - - /** - * The by product period window. - */ - public static final int PERIOD = 17; - - /** - * The total number of classes. - */ - public static final int TOTAL_CLASSES = 18; - - /** - * The instantiated classes. - */ - public static Container[] screens = new Container[TOTAL_CLASSES]; - - /** - * The connection with Litebase. - */ - public static LitebaseConnection driver = LitebaseConnection.getInstance(); - - /** - * An auxiliary date object. - */ - public static Date dateAux = new Date(); - - /** - * An auxiliary StringBuffer for string concatenation. - */ - public static StringBuffer sBuffer = new StringBuffer(); - - /** - * An array of years to be used in the combo box. - */ - public static final String[] years = {"2008", "2009", "2010", "2011", "2012", "2013", "2014", "2015", "2016"}; - - /** - * The info image. - */ - public static Image infoImg; - - /** - * The message box for the info about the application. - */ - public static MessageBox info; - - static - { - Settings.useNewFont = true; - } - - /** - * The application constructor. - */ - public SalesPlus() - { - setUIStyle(Settings.Android); - setBackColor(UIColors.controlsBack = Color.WHITE); - UIColors.messageboxBack = Color.brighter(0x0A246A, 64); - UIColors.messageboxFore = Color.WHITE; - - try - { - infoImg = new Image("images/ic_dialog_info.png"); - } - catch (ImageException exception) {} - catch (IOException exception) {} - - info = new MessageBox("About", "TotalCross Sales Plus | Example program for the|TotalCross SDK. | Created by Celso Marcelo da Silva " - + "| and Juliana Carpes Imperial | www.superwaba.com.br"); - info.footerColor = info.headerColor = UIColors.messageboxBack; - try - { - info.setIcon(infoImg); - } - catch (ImageException exception) {} - - // Instantiates the screens. - screens[MAIN_MENU] = new MainMenu(); - screens[CUSTOMER_MENU] = new CustomerMenu(); - screens[ORDER_MENU] = new OrderMenu(); - screens[PRODUCT_MENU] = new ProductMenu(); - screens[REPORT_MENU] = new ReportMenu(); - screens[SUMMARY_MENU] = new SummaryMenu(); - screens[BY_PRODUCT_MENU] = new ByProductMenu(); - screens[NEW_PRODUCT] = new NewProduct(); - screens[PRODUCT_SEARCH] = new ProductSearch(); - screens[NEW_CUSTOMER] = new NewCustomer(); - screens[CUSTOMER_SEARCH] = new CustomerSearch(); - screens[NEW_ORDER] = new NewOrder(); - screens[ORDER_SEARCH] = new OrderSearch(); - screens[DAY] = new Day(); - screens[MONTH] = new Month(); - screens[DAY2] = new Day2(); - screens[MONTH2] = new Month2(); - screens[PERIOD] = new Period(); - } - - /** - * Called to initialize the User Interface of this container. - */ - public void initUI() - { - ((BaseMenu)screens[MAIN_MENU]).show(); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/Customer.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/Customer.java deleted file mode 100644 index cc67fe771d..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/Customer.java +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -/** - * This is where a customer is specified: a customer must have a name, an identification number, a complete address (address, city, state, country, - * zip), and contact phone numbers. - * - * Sometimes when dealing with large companies there is often a person inside the company that is the contact, which has a name, role in the - * company, and telephone numbers. Therefore, they are also fields of the customer abstract data type. - */ -public class Customer -{ - /** - * Customer name. - */ - public String name; - - /** - * Customer fed id. - */ - public String fedId; - - /** - * Customer address. - */ - public String address; - - /** - * Customer city. - */ - public String city; - - /** - * Customer state. - */ - public String state; - - /** - * Customer country. - */ - public String country; - - /** - * Customer zip address. - */ - public String zip; - - /** - * Customer first telephone number. - */ - public String tel1; - - /** - * Customer second telephone number. - */ - public String tel2; - - /** - * Customer fax. - */ - public String fax; - - /** - * Customer contact name. - */ - public String cName; - - /** - * Customer contact role. - */ - public String role; - - /** - * Customer contact e-mail. - */ - public String email; - - /** - * Customer contact telephone number. - */ - public String cTel; - - /** - * Customer contact mobile telephone number. - */ - public String cel; -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/CustomerDB.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/CustomerDB.java deleted file mode 100644 index ab5e576c1c..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/CustomerDB.java +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -import samples.apps.salesplus.SalesPlus; -import litebase.*; - -/** - * Contains all database operations concerning a customer. - */ -public class CustomerDB -{ - /** - * A prepared statement to insert data into the customer table. - */ - private static PreparedStatement psInsert; - - /** - * A prepared statement to update data of the customer table. - */ - private static PreparedStatement psUpdate; - - /** - * A prepared statement to delete data from the customer table. - */ - private static PreparedStatement psDelete; - - /** - * A prepared statement to select all customers. - */ - private static PreparedStatement psSelectAll; - - /** - * A prepared statement to select a customer given the name. - */ - private static PreparedStatement psSelectName; - - static - { - LitebaseConnection driver = SalesPlus.driver; - - if (!driver.exists("customer")) - { - driver.execute("create table customer(name char(30), fedid char (15) primary key, address char (30), city char(15), state char(10), " - + "country char(15), zip char(10), tel1 char(15), tel2 char(15), fax char(15), cname char(30), role char(20), email char(20), " - + "ctel char(15), cel char(15))"); - driver.execute("CREATE INDEX IDX ON customer(name)"); - } - else if (!driver.isTableProperlyClosed("customer")) - driver.recoverTable("customer"); - - psInsert = driver.prepareStatement("insert into customer values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - psUpdate = driver.prepareStatement("update customer set name = ?, fedid = ?, address = ?, city = ?, state = ?, country = ?, zip = ?, " - + "tel1 = ?, tel2 = ?, fax = ?, cname = ?, role = ?, email = ?, ctel = ?, cel = ? where fedid = ?"); - psDelete = driver.prepareStatement("delete customer where fedid = ?"); - psSelectAll = driver.prepareStatement("select name from customer"); - psSelectName = driver.prepareStatement("select * from customer where name = ?"); - } - - /** - * Inserts or updates a customer in the table. - * - * @param customer The customer to be inserted or updated. - * @param update Indicates if the customer is being updated or inserted. - * @return 1 if the record was updated or inserted; 0 if no record was updated. - */ - public static int write(Customer customer, boolean update) - { - PreparedStatement prepStmt; - - if (getFromName(customer.name) != null) // There can't be repeated names. - return 0; - - if (update) - { - prepStmt = psUpdate; - prepStmt.setString(15, customer.fedId); - } - else - prepStmt = psInsert; - - prepStmt.setString(0, customer.name); - prepStmt.setString(1, customer.fedId); - prepStmt.setString(2, customer.address); - prepStmt.setString(3, customer.city); - prepStmt.setString(4, customer.state); - prepStmt.setString(5, customer.country); - prepStmt.setString(6, customer.zip); - prepStmt.setString(7, customer.tel1); - prepStmt.setString(8, customer.tel2); - prepStmt.setString(9, customer.fax); - prepStmt.setString(10, customer.cName); - prepStmt.setString(11, customer.role); - prepStmt.setString(12, customer.email); - prepStmt.setString(13, customer.cTel); - prepStmt.setString(14, customer.cel); - - return prepStmt.executeUpdate(); - } - - - /** - * Removes a customer from the customer table - * - * @param customer The customer to be removed. - */ - public static void remove(Customer customer) - { - psDelete.setString(0, customer.fedId); - psDelete.executeUpdate(); - } - - /** - * Returns all the customer names. - * - * @return An array with all the customer names. - */ - public static String[] readAllNames() - { - ResultSet resultSet = psSelectAll.executeQuery(); - String[] strings = new String[resultSet.getRowCount()]; - int i = 0; - - while(resultSet.next()) - strings[i++] = resultSet.getString(1); - resultSet.close(); - return strings; - } - - - /** - * Returns a specific customer - * - * @param name The name of the customer. - * @return The customer or null if the customer was not found. - */ - public static Customer getFromName(String name) - { - psSelectName.setString(0, name); - - ResultSet resultSet = psSelectName.executeQuery(); - - if (resultSet.first()) - { - Customer customer = new Customer(); - customer.name = resultSet.getString(1); - customer.fedId = resultSet.getString(2); - customer.address = resultSet.getString(3); - customer.city = resultSet.getString(4); - customer.state = resultSet.getString(5); - customer.country = resultSet.getString(6); - customer.zip = resultSet.getString(7); - customer.tel1 = resultSet.getString(8); - customer.tel2 = resultSet.getString(9); - customer.fax = resultSet.getString(10); - customer.cName = resultSet.getString(11); - customer.role = resultSet.getString(12); - customer.email = resultSet.getString(13); - customer.cTel = resultSet.getString(14); - customer.cel = resultSet.getString(15); - return customer; - } - resultSet.close(); - - return null; - } - - /** - * Searches for a customer matching the specific search fields. - * - * @param name The name of the customer. - * @return An array containing the result of the search. - */ - public static Customer[] searchCustomers(String name) - { - psSelectName.setString(0, name); - ResultSet resultSet = psSelectName.executeQuery(); - Customer[] customers = new Customer[resultSet.getRowCount()]; - int i = 0; - Customer customer; - - while (resultSet.next()) - { - (customer = customers[i++] = new Customer()).name = resultSet.getString(1); - customer.fedId = resultSet.getString(2); - customer.address = resultSet.getString(3); - customer.city = resultSet.getString(4); - customer.state = resultSet.getString(5); - customer.country = resultSet.getString(6); - customer.zip = resultSet.getString(7); - customer.tel1 = resultSet.getString(8); - customer.tel2 = resultSet.getString(9); - customer.fax = resultSet.getString(10); - customer.cName = resultSet.getString(11); - customer.role = resultSet.getString(12); - customer.email = resultSet.getString(13); - customer.cTel = resultSet.getString(14); - customer.cel = resultSet.getString(15); - } - resultSet.close(); - - return customers; - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/ItemOrder.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/ItemOrder.java deleted file mode 100644 index aeb5a6d90d..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/ItemOrder.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -/** - * This is where an item of an order is specified: an item inside an order must have an associated order id (so that it is possible to know which - * item is related to which order), an id of the product (so that it is possible to know which product is this item), the quantity of items related - * in that order, the price of one unit at the time that the order was done (products unit price can change), and the price of the total amount - * (quantity * unit price). - */ -public class ItemOrder -{ - /** - * The order id. - */ - public int orderId; - - /** - * The product id. - */ - public String productId; - - /** - * The quantity of an item of an order. - */ - public int quant; - - /** - * The unit price of the product when included in the order multiplied by 100. - */ - public int unitPrice; - - /** - * The total amount of money spent buying the product in the order multiplied by 100. - */ - public int totalAmount; -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/ItemOrderDB.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/ItemOrderDB.java deleted file mode 100644 index d6941647ea..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/ItemOrderDB.java +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -import samples.apps.salesplus.SalesPlus; -import litebase.*; - -/** - * Contains all database operations concerning an order item. - */ -public class ItemOrderDB -{ - /** - * A prepared statement to insert data into the order item table. - */ - private static PreparedStatement psInsert; - - /** - * A prepared statement to update data of the order item table. - */ - private static PreparedStatement psUpdate; - - /** - * A prepared statement to delete data from the order item table. - */ - private static PreparedStatement psDelete; - - /** - * A prepared statement to delete all items from an order. - */ - private static PreparedStatement psDeleteAll; - - /** - * A prepared statement to select all items of an order. - */ - private static PreparedStatement psSelectItems; - - /** - * A prepared statement to check if there is a product in an order item. - */ - private static PreparedStatement psSelectProduct; - - static - { - LitebaseConnection driver = SalesPlus.driver; - - if (!driver.exists("itemorder")) - { - driver.execute("create table itemorder(orderid int, productid char(20), quant int, unitPrice int, totalAmount int)"); - driver.execute("CREATE INDEX IDX ON itemorder(orderid)"); - driver.execute("CREATE INDEX IDX ON itemorder(productid)"); - } - else if (!driver.isTableProperlyClosed("itemorder")) - driver.recoverTable("itemorder"); - - psInsert = driver.prepareStatement("insert into itemorder values (?, ?, ?, ?, ?)"); - psUpdate = driver.prepareStatement("update itemorder set orderid = ?, productid = ?, quant = ?, unitPrice = ?, totalAmount = ? " - + "where productid = ?"); - psDelete = driver.prepareStatement("delete itemorder where productid = ? and orderid = ?"); - psDeleteAll = driver.prepareStatement("delete itemorder where orderid = ?"); - psSelectItems = driver.prepareStatement("select * from itemorder where orderid = ?"); - psSelectProduct = driver.prepareStatement("select * from itemorder where productid = ?"); - } - - /** - * Inserts or updates an item of an order in the table. - * - * @param item The item related to the order. - * @param update Indicates if the item is being updated or inserted. - */ - public static void write(ItemOrder item, boolean update) - { - PreparedStatement prepStmt; - - if (update) - { - prepStmt = psUpdate; - prepStmt.setString(5, item.productId); - } - else - prepStmt = psInsert; - - prepStmt.setInt(0, item.orderId); - prepStmt.setString(1, item.productId); - prepStmt.setInt(2, item.quant); - prepStmt.setInt(3, item.unitPrice); - prepStmt.setInt(4, item.totalAmount); - prepStmt.executeUpdate(); - } - - /** - * Searches for a specific order/product and removes it. - * - * @param orderId The order id; - * @param prodCode The product code. - */ - public static void remove(int orderId, String prodCode) - { - psDelete.setString(0, prodCode); - psDelete.setInt(1, orderId); - psDelete.executeUpdate(); - } - - /** - * Searches for a specific order id and removes all items containing it. - * - * @param orderId The order id; - */ - public static void removeAll(int orderId) - { - psDeleteAll.setInt(0, orderId); - psDeleteAll.executeUpdate(); - } - - /** - * Read all order items related to a specific order id. - * - * @param orderId The order id. - * @return An array containing all items related to that order. - */ - public static ItemOrder[] readAll(int orderId) - { - psSelectItems.setInt(0, orderId); - - ResultSet resultSet = psSelectItems.executeQuery(); - ItemOrder[] items = new ItemOrder[resultSet.getRowCount()]; - int i = 0; - ItemOrder item; - - while (resultSet.next()) - { - (item = items[i++] = new ItemOrder()).orderId = resultSet.getInt(1); - item.productId = resultSet.getString(2); - item.quant = resultSet.getInt(3); - item.unitPrice = resultSet.getInt(4); - item.totalAmount = resultSet.getInt(5); - } - resultSet.close(); - - return items; - } - - - /** - * Checks if there is an order whose list has a specific product as its related item. This is - * useful when it is desired to delete a product but it is already related to an order (consistency). - * - * @param product The product. - * @return true if there is at least one item that has the product or false, otherwise. - */ - public static boolean hasProduct(Product product) - { - psSelectProduct.setString(0, product.code); - ResultSet resultSet = psSelectProduct.executeQuery(); - boolean ret = resultSet.first(); - - resultSet.close(); - return ret; - } - -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/Order.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/Order.java deleted file mode 100644 index 042b0d728d..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/Order.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -/** - * This where an order is specified: an order must have a unique identification number, it also needs a customer which is related to the order - * (each order has its own related customer and only one), the date in which the order was issued, and the total price of the order (each - * items quantity * unit price altogether). - */ -public class Order -{ - /** - * The order id. - */ - public int orderId; - - /** - * The order customer. - */ - public String customer; - - /** - * The order date. - */ - public int date; - - /** - * The order total amount (each items quantity * unit price altogether). - */ - public int totalAmount; -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/OrderDB.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/OrderDB.java deleted file mode 100644 index ad46a8f47c..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/OrderDB.java +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -import samples.apps.salesplus.SalesPlus; -import totalcross.sys.Settings; -import totalcross.util.*; -import litebase.*; - -/** - * Contains all database operations concerning an order. - */ -public class OrderDB -{ - /** - * A prepared statement to insert data into the order table. - */ - private static PreparedStatement psInsert; - - /** - * A prepared statement to update data of the order table. - */ - private static PreparedStatement psUpdate; - - /** - * A prepared statement to select all orders issued in a given date. - */ - private static PreparedStatement psSelectDate; - - /** - * A prepared statement to select all orders issued in a given date interval. - */ - private static PreparedStatement psSelectDays; - - /** - * A prepared statement to delete an order. - */ - private static PreparedStatement psDelete; - - /** - * A prepared statement to select all orders of a given customer issued in a given date. - */ - private static PreparedStatement psSelectNameDate; - - /** - * A prepared statement to select all orders of a given customer. - */ - private static PreparedStatement psSelectName; - - /** - * A prepared statement to select an order given its id. - */ - private static PreparedStatement psSelectId; - - static - { - LitebaseConnection driver = SalesPlus.driver; - - if (!driver.exists("orders")) - { - driver.execute("create table orders(customer char(20), intdate int, totalAmount int)"); - driver.execute("CREATE INDEX IDX ON orders(rowid)"); - driver.execute("CREATE INDEX IDX ON orders(intdate)"); - } - else if (!driver.isTableProperlyClosed("orders")) - driver.recoverTable("orders"); - - psInsert = driver.prepareStatement("insert into orders values (?, ?, ?)"); - psUpdate = driver.prepareStatement("update orders set customer = ?, intdate = ?, totalAmount = ? where rowid = ?"); - psSelectDate = driver.prepareStatement("select rowid, customer, intdate, totalamount from orders where intdate = ?"); - psSelectDays = driver.prepareStatement("select rowid, customer, intdate, totalamount from orders where intdate >= ? and intdate <= ?"); - psDelete = driver.prepareStatement("delete orders where rowid = ?"); - psSelectNameDate = driver.prepareStatement("select rowid, customer, intdate, totalamount from orders where customer = ? and intdate = ?"); - psSelectName = driver.prepareStatement("select rowid, customer, intdate, totalamount from orders where customer = ?"); - psSelectId = driver.prepareStatement("select rowid, customer, intdate, totalamount from orders where rowid = ?"); - } - - /** - * Inserts or updates an order. - * - * @param order The item related to the order. - * @param update Indicates if the order is being updated or inserted. - */ - public static void write(Order order, boolean update) - { - PreparedStatement prepStmt; - - if (update) - { - prepStmt = psUpdate; - prepStmt.setInt(3, order.orderId); - } - else - prepStmt = psInsert; - - prepStmt.setString(0, order.customer); - prepStmt.setInt(1, order.date); - prepStmt.setInt(2, order.totalAmount); - prepStmt.executeUpdate(); - } - - /** - * Gets all the orders issued in a given date. - * - * @param date The desired date. - * @return An array with all orders issued in the given date. - */ - public static Order[] getFromDate(int date) - { - psSelectDate.setInt(0, date); - - ResultSet resultSet = psSelectDate.executeQuery(); - Order[] orders = new Order[resultSet.getRowCount()]; - int i = 0; - Order order; - - while (resultSet.next()) - { - (order = orders[i++] = new Order()).orderId = resultSet.getInt(1); - order.customer = resultSet.getString(2); - order.date = date; - order.totalAmount = resultSet.getInt(4); - } - resultSet.close(); - - return orders; - } - - /** - * Fetches all order issued in all days of a given month and year. - * - * @param year The desired year. - * @param month The desired month. - * @return All orders issued in the given date interval. - */ - public static Order[] readAllDays(int year, int month) - { - psSelectDays.setInt(0, year = (year * 10000 + month * 100 + 1)); - psSelectDays.setInt(1, year + 30); - - ResultSet resultSet = psSelectDays.executeQuery(); - Order[] orders = new Order[resultSet.getRowCount()]; - Order order; - int i = 0; - - while (resultSet.next()) - { - (order = orders[i++] = new Order()).orderId = resultSet.getInt(1); - order.customer = resultSet.getString(2); - order.date = resultSet.getInt(3); - order.totalAmount = resultSet.getInt(4); - } - - return orders; - } - - /** - * Removes an order order table. First all the items must be removed and just then the order itself can be removed (consistency). - * - * @param order The order to be removed. - */ - public static void removeOrder(Order order) - { - psDelete.setInt(0, order.orderId); - psDelete.executeUpdate(); - } - - /** - * Search for an order with specific name and date conditions. - * - * @param name The name of the customer related to the order - * @param date The date in which the order was issued - * @return An array with all orders of the given customer issued in the given date. - */ - public static Order[] search(String name, String date) throws InvalidDateException - { - SalesPlus.dateAux.set(date, Settings.dateFormat); - - int dateInt = SalesPlus.dateAux.getDateInt(); - - psSelectNameDate.setString(0, name); - psSelectNameDate.setInt(1, dateInt); - - ResultSet resultSet = psSelectNameDate.executeQuery(); - Order[] orders = new Order[resultSet.getRowCount()]; - Order order; - int i = 0; - - while (resultSet.next()) - { - (order = orders[i++] = new Order()).orderId = resultSet.getInt(1); - order.customer = name; - order.date = dateInt; - order.totalAmount = resultSet.getInt(4); - } - resultSet.close(); - - return orders; - } - - /** - * Checks if an specific customer has an order.. - * - * @param customer The customer being searched. - * @return true if the customer has issued an order; false, otherwise. - */ - public static boolean hasOrder(Customer customer) - { - psSelectName.setString(0, customer.name); - - ResultSet resultSet = psSelectName.executeQuery(); - boolean ret = resultSet.first(); - - resultSet.close(); - return ret; - } - - /** - * Returns the order associated with its unique order id. - * - * @param id Unique order id. - * @return The order if found or null, otherwise. - */ - public static Order getFromId(int id) - { - psSelectId.setInt(0, id); - - ResultSet resultSet = psSelectId.executeQuery(); - Order order = null; - - if (resultSet.first()) - { - (order = new Order()).orderId = id; - order.customer = resultSet.getString(2); - order.date = resultSet.getInt(3); - order.totalAmount = resultSet.getInt(4); - } - resultSet.close(); - - return order; - } - - /** - * Retrieves the order id of the last order to calculate a new order id. - */ - public static int getLastOrderId() - { - return SalesPlus.driver.getCurrentRowId("orders"); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/Product.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/Product.java deleted file mode 100644 index f6cfb81195..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/Product.java +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -/** - * This where a product is specified: a product must have a name and an identification code, - * a short description of what the product is and its current price per unit. - */ -public class Product -{ - /** - * The product name. - */ - public String name; - - /** - * The product code. - */ - public String code; - - /** - * The product description. - */ - public String descr; - - /** - * The product unit price. - */ - public int unitPrice; -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/db/ProductDB.java b/LitebaseSDK/src/java/samples/apps/salesplus/db/ProductDB.java deleted file mode 100644 index 15a6179b6c..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/db/ProductDB.java +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.db; - -import samples.apps.salesplus.SalesPlus; -import litebase.*; - -/** - * Contains all database operations concerning a product. - */ -public class ProductDB -{ - /** - * A prepared statement to insert data into the product table. - */ - private static PreparedStatement psInsert; - - /** - * A prepared statement to update data of the product table. - */ - private static PreparedStatement psUpdate; - - /** - * A prepared statement to select all product names. - */ - private static PreparedStatement psSelectNames; - - /** - * A prepared statement to delete an product. - */ - private static PreparedStatement psDelete; - - /** - * A prepared statement to select a product given its code. - */ - private static PreparedStatement psSelectCode; - - /** - * A prepared statement to select a product given its name. - */ - private static PreparedStatement psSelectName; - - /** - * A prepared statement to select a product given its name and code. - */ - private static PreparedStatement psSelectCodeName; - - static - { - LitebaseConnection driver = SalesPlus.driver; - - if (!driver.exists("product")) - { - driver.execute("create table product(name char(30), code char(10) primary key, descr char(50), unitprice int)"); - driver.execute("CREATE INDEX IDX ON product(NAME)"); - } - else if (!driver.isTableProperlyClosed("product")) - driver.recoverTable("product"); - - psInsert = driver.prepareStatement("insert into product values (?, ?, ?, ?)"); - psUpdate = driver.prepareStatement("update product set name = ?, code = ?, descr = ?, unitprice = ? where code = ?"); - psSelectNames = driver.prepareStatement("select name from product"); - psDelete = driver.prepareStatement("delete product where code = ?"); - psSelectCode = driver.prepareStatement("select * from product where code = ?"); - psSelectName = driver.prepareStatement("select * from product where name = ?"); - psSelectCodeName = driver.prepareStatement("select * from product where name = ? and code = ?"); - } - - /** - * Inserts or updates a product. - * - * @param product The product to be inserted or updated. - * @param update Indicates if the product is being updated or inserted. - * @return 1 if the record was updated or inserted; 0 if no record was updated. - */ - public static int write(Product product, boolean update) - { - PreparedStatement prepStmt; - - if (getFromName(product.name) != null) // There can't be repeated names. - return 0; - - if (update) - { - prepStmt = psUpdate; - prepStmt.setString(4, product.code); - } - else - prepStmt = psInsert; - - prepStmt.setString(0, product.name); - prepStmt.setString(1, product.code); - prepStmt.setString(2, product.descr); - prepStmt.setInt(3, product.unitPrice); - return prepStmt.executeUpdate(); - } - - /** - * Reads the names of all products of the product table. - * - * @return An array holding all product names available. - */ - public static String[] readAllNames() - { - ResultSet resultSet = psSelectNames.executeQuery(); - String[] strings = new String[resultSet.getRowCount()]; - int i = 0; - - while(resultSet.next()) - strings[i++] = resultSet.getString(1); - resultSet.close(); - - return strings; - } - - /** - * Remove a specific product from the table. - * - * @param product The product to be removed - */ - public static void remove(Product product) - { - psDelete.setString(0, product.code); - psDelete.executeUpdate(); - } - - /** - * Retrieves a product information based on the product code. - * - * @param prodCode The product code. - * @return The product if found or null if none is found. - */ - public static Product getFromCode(String prodCode) - { - psSelectCode.setString(0, prodCode); - - ResultSet resultSet = psSelectCode.executeQuery(); - Product product = null; - - if (resultSet.first()) - { - (product = new Product()).name = resultSet.getString(1); - product.code = prodCode; - product.descr = resultSet.getString(3); - product.unitPrice = resultSet.getInt(4); - } - resultSet.close(); - - return product; - } - - /** - * Retrieves a product information based on the product name. - * - * @param prodName The product name. - * @return The product if found or null if none is found. - */ - public static Product getFromName(String prodName) // infoName - { - psSelectName.setString(0, prodName); - - ResultSet resultSet = psSelectName.executeQuery(); - Product product = null; - - if (resultSet.first()) - { - (product = new Product()).name = prodName; - product.code = resultSet.getString(2); - product.descr = resultSet.getString(3); - product.unitPrice = resultSet.getInt(4); - } - resultSet.close(); - - return product; - } - - /** - * Search for a product matching the searching fields. - * - * @param prodName The name of the product. - * @param prodCode The code of the product. - * @return An array of products. - */ - public static Product[] search(String prodName, String prodCode) - { - psSelectCodeName.setString(0, prodName); - psSelectCodeName.setString(1, prodCode); - - ResultSet resultSet = psSelectCodeName.executeQuery(); - Product[] products = new Product[resultSet.getRowCount()]; - Product product; - int i = 0; - - while (resultSet.next()) - { - (product = products[i++] = new Product()).name = prodName; - product.code = prodCode; - product.descr = resultSet.getString(3); - product.unitPrice = resultSet.getInt(4); - } - resultSet.close(); - - return products; - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/images/ic_dialog_info.png b/LitebaseSDK/src/java/samples/apps/salesplus/images/ic_dialog_info.png deleted file mode 100644 index efee1efa47..0000000000 Binary files a/LitebaseSDK/src/java/samples/apps/salesplus/images/ic_dialog_info.png and /dev/null differ diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/BaseMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/BaseMenu.java deleted file mode 100644 index c2e15c23da..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/BaseMenu.java +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui; - -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.res.Resources; -import totalcross.sys.*; -import totalcross.ui.font.*; -import totalcross.ui.gfx.Color; -import totalcross.util.*; - -/** - * Base class for menus (buttons placed vertically). - */ -public abstract class BaseMenu extends Container -{ - /** - * Button captions. - */ - private String[] captions; - - /** - * Button ids. - */ - private int[] ids; - - /** - * Indicates if a menu is the root menu or an intermediate menu. - */ - private boolean isRoot; - - /** - * A header bar. - */ - private Bar headerBar; - - /** - * A container stack for going from and back windows. - */ - private static Vector containerStack = new Vector(4); - - /** - * Constructor for a base menu. - * - * @param newCaptions The button captions. - * @param newIds The button ids. - * @param newIsRoot Indicates if the menu is a root or an intermediate menu. - */ - public BaseMenu(String[] newCaptions, int[] newIds, boolean newIsRoot) - { - captions = newCaptions; - ids = newIds; - isRoot = newIsRoot; - } - - /** - * Initializes the user interface. - */ - public void initUI() - { - Button button = null; - Container container = new Container(); - Font usedFont = Font.getFont(font.name, true, Font.BIG_SIZE); - int length = ids.length, - width = (int)(Settings.screenWidth * 0.75), // Buttons fill 75% of the page. - gap = Settings.screenWidth == 160 ? 2 : 4, - i = -1; - - headerBar = new Bar("Sales Plus"); - headerBar.setFont(font.adjustedBy(2, true)); - headerBar.setBackForeColors(0x0A246A, Color.WHITE); - headerBar.addButton(SalesPlus.infoImg); - headerBar.addButton(isRoot? Resources.exit : Resources.back); - add(headerBar, LEFT, 0, FILL, PREFERRED); - headerBar.appId = isRoot? 999 : 0; - - add(container); - - // Places all controls inside another container so that this container can be centered on screen. - Button.commonGap = 4; - setFont(usedFont.asBold()); - - // Adds the buttons. - container.setRect(0, 0, width, 1000); // Creates a container with a huge height. - - while (++i < length) - { - container.add(button = new Button(captions[i])); - button.setFont(usedFont); - button.setRect(0, AFTER + gap, width, PREFERRED); - button.appId = ids[i]; - } - - container.setRect(CENTER, CENTER, width, button.getRect().y2() + gap); // Sets the height to the correct size. - Button.commonGap = 0; - } - - /** - * Called to process posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target instanceof Bar) - { - switch (headerBar.getSelectedIndex()) - { - case 1: - SalesPlus.info.popup(); - break; - case 2: - back(); // Goes to the last container or exists the application. - } - } - else if (event.target instanceof Button) - { - Container container = SalesPlus.screens[((Button)event.target).appId]; - if (container instanceof BaseMenu) - ((BaseMenu)container).show(); - else - Window.getTopMost().swap(container); - } - - } - } - - /** - * Shows a window. - */ - public void show() - { - containerStack.push(this); // Pushes itself. - Window.getTopMost().swap(this); - } - - /** - * Goes back to the previous window. - */ - public void back() - { - try - { - containerStack.pop(); // Pops itself. - Window.getTopMost().swap((Container)containerStack.peek()); - } - catch (ElementNotFoundException exception) - { - MainWindow.exit(0); // This is the last screen, so just exits the application. - } - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/MainMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/MainMenu.java deleted file mode 100644 index c019c13b5a..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/MainMenu.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui; - -import samples.apps.salesplus.SalesPlus; - -/** - * The main menu, the one that is shown when the program is openned. - */ -public class MainMenu extends BaseMenu -{ - /** - * The constructor - */ - public MainMenu() - { - super(new String[]{ "Customers", "Orders", "Products", "Reports"}, - new int[]{SalesPlus.CUSTOMER_MENU, SalesPlus.ORDER_MENU, SalesPlus.PRODUCT_MENU,SalesPlus.REPORT_MENU}, true); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/CustomerMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/CustomerMenu.java deleted file mode 100644 index 3a506b8522..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/CustomerMenu.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.customer; - -import samples.apps.salesplus.*; -import samples.apps.salesplus.ui.*; - -/** - * The customer menu. - */ -public class CustomerMenu extends BaseMenu -{ - /** - * The constructor. - */ - public CustomerMenu() - { - super(new String[]{"New Customer", "Search"}, - new int[]{SalesPlus.NEW_CUSTOMER, SalesPlus.CUSTOMER_SEARCH}, false); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/CustomerSearch.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/CustomerSearch.java deleted file mode 100644 index 827d6d1116..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/CustomerSearch.java +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.customer; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.ui.gfx.Color; - -/** - * The customer search menu. - */ -public class CustomerSearch extends Container -{ - /** - * The back button; - */ - private Button btnBack; - - /** - * The search button. - */ - private Button btnSearch; - - /** - * The edit button. - */ - private Button btnEdit; - - /** - * The remove button. - */ - private Button btnRemove; - - /** - * The result grid. - */ - private Grid grid; - - /** - * The name edit. - */ - private Edit edName; - - /** - * Initializes the user interface. - */ - public void initUI() - { - Button.commonGap = 1; - - // Name edit and label. - add(new Label("Name: "), LEFT + 5, TOP + 5); - add(edName = new Edit(""), AFTER, SAME, PREFERRED - 5, PREFERRED); - - // Buttons. - add(btnSearch = new Button("Search"), RIGHT - 5, AFTER + 5, PREFERRED, PREFERRED); - add(btnEdit = new Button("View"), RIGHT - 1, BOTTOM - 1); - add(btnBack = new Button("Back"), BEFORE - 2, SAME); - add(btnRemove = new Button("Remove"), BEFORE - 2, SAME); - - // Grid. - add(grid = new Grid(new String[]{"Name", "City", "State"}, - new int[]{fm.stringWidth("xxxxxxxxxxx"), fm.stringWidth("xxxxxxxxxx"), fm.stringWidth("xxx")}, - new int[]{LEFT, LEFT, CENTER}, false), LEFT + 5, AFTER + 5, FILL - 5, FIT, btnSearch); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - Button.commonGap = 0; - } - - /** - * Fetches a selected customer. - * - * @return The customer data. - */ - private Customer getSelectedCustomer() - { - int idx = grid.getSelectedIndex(); - if (idx < 0) - return null; - return CustomerDB.getFromName(grid.getItem(idx)[0]); - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnBack) // Pressed back button: goes to the customer menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.CUSTOMER_MENU]); - else if (event.target == btnSearch) // Pressed search button: searches for a customer. - onAddAgain(); - else if (event.target == btnRemove) // Pressed removed button: removes the selected customer if possible. - { - Customer customer = getSelectedCustomer(); - if (customer != null) - { - if (!OrderDB.hasOrder(customer)) // The customer can't have a order. - { - CustomerDB.remove(customer); - onAddAgain(); - } - else - new MessageBox("Error", "There are orders that belong to | this customer. If you really want |" - + "to remove it, delete the orders first.").popupNonBlocking(); - } - } - else - if (event.target == btnEdit) // Pressed edit button: edits the selected customer. - { - Customer cli = getSelectedCustomer(); - if (cli != null) - { - NewCustomer.sentCustomer = cli; - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.NEW_CUSTOMER]); - } - } - } - } - - /** - * Updates the grid searching the customer again. - */ - public void onAddAgain() - { - Customer[] customers = CustomerDB.searchCustomers(edName.getText()); - int count = customers.length; - - grid.removeAllElements(); - - if (count != 0) - { - String[][] strings = new String[count][3]; - Customer customer; - int i = count; - - while (--i >= 0) - { - strings[i][0] = (customer = customers[i]).name; - strings[i][1] = customer.city; - strings[i][2] = customer.name; - } - grid.setItems(strings); - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewContact.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewContact.java deleted file mode 100644 index 3d7bb1b0c6..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewContact.java +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.customer; - -import samples.apps.salesplus.db.*; -import totalcross.ui.*; -import totalcross.ui.gfx.Color; - -/** - * The contact information tab. - */ -public class NewContact extends Container -{ - /** - * The contact name edit. - */ - private Edit edName; - - /** - * The contact role edit. - */ - private Edit edRole; - - /** - * The contact tel edit. - */ - private Edit edCTel; - - /** - * The contact cel edit. - */ - private Edit edCel; - - /** - * The contact e-mail edit. - */ - private Edit edEmail; - - /** - * Initializes the user interface. - */ - public void initUI() - { - setBackColor(Color.WHITE); - - // Name label and edit. - add(new Label("Name: "), LEFT + 5, TOP + 5); - add(edName = new Edit(""), AFTER + 20, SAME, PREFERRED - 5, PREFERRED); - - int initX = edName.getRect().x; - - // Role label and edit. - add(new Label("Role: "), LEFT + 5, AFTER + 2); - add(edRole = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // E-mail label and edit. - add(new Label("Email: "), LEFT + 5, AFTER + 2); - add(edEmail = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Contact tel label and edit. - add(new Label("Tel.: "), LEFT + 5, AFTER + 2); - add(edCTel = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Cel label and edit. - add(new Label("Cel.: "), LEFT + 5, AFTER + 2); - add(edCel = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - } - - /** - * Sets the customer contact data in the edits. - * - * @param customer The customer data. - */ - public void setEdit(Customer customer) - { - edName.setText(customer.cName); - edRole.setText(customer.role); - edEmail.setText( customer.email); - edCTel.setText(customer.cTel); - edCel.setText(customer.cel); - } - - /** - * Gets the customer contact data to set the customer data. - * - * @param customer The customer data. - */ - public void getFromEdit(Customer customer) - { - customer.cName = edName.getText(); - customer.role = edRole.getText(); - customer.email = edEmail.getText(); - customer.cTel = edCTel.getText(); - customer.cel = edCel.getText(); - } - - /** - * Clears all customer contact data edits. - */ - public void cleanEdit() - { - edName.setText(""); - edRole.setText(""); - edCTel.setText(""); - edEmail.setText(""); - edCel.setText(""); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewCustomer.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewCustomer.java deleted file mode 100644 index 775581d6af..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewCustomer.java +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.customer; - -import litebase.PrimaryKeyViolationException; -import samples.apps.salesplus.*; -import samples.apps.salesplus.db.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.ui.gfx.*; - -/** - * The new customer menu. - */ -public class NewCustomer extends Container -{ - /** - * An auxiliary customer. - */ - static Customer sentCustomer; - - /** - * A tab for general customer data. - */ - private NewGeneral newGeneral; - - /** - * A tab for contact customer data. - */ - private NewContact newContact; - - /** - * A tabbed container. - */ - private TabbedContainer tabCont; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The clear button. - */ - private Button btnClear; - - /** - * The insert or update button. - */ - private Button btnInsert; - - /** - * Initializes the user interface. - */ - public void initUI() - { - Button.commonGap = 1; - - // The tabbed container. - add(tabCont = new TabbedContainer(new String[]{"General", "Contact"}), LEFT, TOP, FILL, FILL); - tabCont.setBorderStyle(Window.NO_BORDER); - tabCont.setBackColor(Color.BRIGHT); - tabCont.setContainer(0, newGeneral = new NewGeneral()); - tabCont.setContainer(1, newContact = new NewContact()); - - // The buttons. - add(btnInsert = new Button("Insert "), RIGHT - 2, BOTTOM - 2); - add(btnBack = new Button("Back"), BEFORE - 2, SAME); - add(btnClear = new Button("Clear"), BEFORE - 2, SAME); - - onAddAgain(); - } - - /** - * Empties all edit boxes and set the active panel to the first one. - */ - public void cleanEdit() - { - newGeneral.cleanEdit(); - newContact.cleanEdit(); - tabCont.setActiveTab(0); - } - - /** - * Called to process posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnClear) // Pressed clear button: clears all data. - cleanEdit(); - else if (event.target == btnBack) // Pressed back button: goes to the previous menu. - { - if (sentCustomer != null) // There is a selected customer, goes to the customer search - { - sentCustomer = null; - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.CUSTOMER_SEARCH]); - } - else // No customer were selected, goes to the parent menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.CUSTOMER_MENU]); - } - else if (event.target == btnInsert) // Pressed insert button: checks data and inserts or updates customer. - { - Customer customer = new Customer(); - StringBuffer sBuffer = SalesPlus.sBuffer; - - newGeneral.getFromEdit(customer); - newContact.getFromEdit(customer); - sBuffer.setLength(0); - - if (customer.name.length() == 0) - sBuffer.append("Name |"); - if (customer.fedId.length() == 0) - sBuffer.append("Fed. ID |"); - if (customer.address.length() == 0) - sBuffer.append("Address |"); - if (customer.city.length() == 0) - sBuffer.append("City |"); - if (customer.state.length() == 0) - sBuffer.append("State |"); - if (customer.country.length() == 0) - sBuffer.append("Country |"); - if (customer.zip.length() == 0) - sBuffer.append("ZIP |"); - if (customer.tel1.length() == 0) - sBuffer.append("tel1 "); - - if (sBuffer.length() > 0) - new MessageBox("Error", "Please fill in the following fields:|"+sBuffer.toString()).popupNonBlocking(); - else - { - try - { - if (CustomerDB.write(customer, sentCustomer != null) == 0) // Inserts or updates customer. - { - new MessageBox("Error", "Customer name can't be repeated.").popup(); - return; - } - } - catch (PrimaryKeyViolationException exception) - { - new MessageBox("Error", "Fed. ID already exists").popup(); - return; - } - - cleanEdit(); - if (sentCustomer != null) // The user may want to update more customers. - { - sentCustomer = null; - new MessageBox("Message", "Customer updated").popup(); - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.CUSTOMER_SEARCH]); - } - else - new MessageBox("Message", "Customer inserted").popupNonBlocking(); - } - } - } - } - - /** - * Sets the new customer menu to insert or update a customer. - */ - public void onAddAgain() - { - if (sentCustomer == null) // Insert a customer: clears the edits. - { - cleanEdit(); - btnInsert.setText("Insert"); - } - else // Update a customer: sets the edits with the selected customer data. - { - btnInsert.setText("Update"); - newGeneral.setEdit(sentCustomer); - newContact.setEdit(sentCustomer); - } - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewGeneral.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewGeneral.java deleted file mode 100644 index 7f3f84bf63..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/customer/NewGeneral.java +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.customer; - -import samples.apps.salesplus.db.*; -import totalcross.ui.*; -import totalcross.ui.gfx.*; - -/** - * The general information tab. - */ -public class NewGeneral extends Container -{ - /** - * The customer name edit. - */ - private Edit edName; - - /** - * The customer Fed. ID edit. - */ - private Edit edFedId; - - /** - * The customer address edit. - */ - private Edit edAddress; - - /** - * The customer city edit. - */ - private Edit edCity; - - /** - * The customer state edit. - */ - private Edit edState; - - /** - * The customer country edit. - */ - private Edit edCountry; - - /** - * The customer zip code edit. - */ - private Edit edZip; - - /** - * The customer first phone number edit. - */ - private Edit edTel1; - - /** - * The customer second phone number edit. - */ - private Edit edTel2; - - /** - * The customer fax edit. - */ - private Edit edFax; - - /** - * Initializes the user interface. - */ - public void initUI() - { - setBackColor(Color.WHITE); - - // Name label and edit. - add(new Label("Name: "), LEFT + 5, TOP + 5); - add(edName = new Edit(""), AFTER + 20, SAME, PREFERRED - 5, PREFERRED); - - int initX = edName.getRect().x; - - // Fed. ID label and edit. - add(new Label("Fed. ID: "), LEFT + 5, AFTER + 2); - add(edFedId = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Address label and edit. - add(new Label("Address: "), LEFT + 5, AFTER + 2); - add(edAddress = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // City label and edit. - add(new Label("City: "), LEFT + 5, AFTER + 2); - add(edCity = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // State label and edit. - add(new Label("State: "), LEFT + 5, AFTER + 2); - add(edState = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Country label and edit. - add(new Label("Country: "), LEFT + 5, AFTER + 2); - add(edCountry = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Zip code label and edit. - add(new Label("ZIP: "), LEFT + 5, AFTER + 2); - add(edZip = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Phone number 1 label and edit. - add(new Label("Tel. 1: "), LEFT + 5, AFTER + 2); - add(edTel1 = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Phone number 2 label and edit. - add(new Label("Tel. 2: "), LEFT + 5, AFTER + 2); - add(edTel2 = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - - // Fax label and edit. - add(new Label("Fax: "), LEFT + 5, AFTER + 2); - add(edFax = new Edit(""), initX, SAME, PREFERRED - 5, PREFERRED); - } - - /** - * Sets the customer general data in the edits. - * - * @param customer The customer data. - */ - public void setEdit(Customer customer) - { - edName.setText(customer.name); - edFedId.setText(customer.fedId); - edAddress.setText(customer.address); - edCity.setText(customer.city); - edState.setText(customer.state); - edCountry.setText(customer.country); - edZip.setText(customer.zip); - edTel1.setText(customer.tel1); - edTel2.setText(customer.tel2); - edFax.setText(customer.fax); - edFedId.setEditable(false); - } - - /** - * Gets the customer general data to set the customer data. - * - * @param customer The customer data. - */ - public void getFromEdit(Customer customer) - { - customer.name = edName.getText(); - customer.fedId = edFedId.getText(); - customer.address = edAddress.getText(); - customer.city = edCity.getText(); - customer.state = edState.getText(); - customer.country = edCountry.getText(); - customer.zip = edZip.getText(); - customer.tel1 = edTel1.getText(); - customer.tel2 = edTel2.getText(); - customer.fax = edFax.getText(); - } - - /** - * Clears all customer general data edits. - */ - public void cleanEdit() - { - edName.setText(""); - edFedId.setText(""); - edAddress.setText(""); - edCity.setText(""); - edState.setText(""); - edCountry.setText(""); - edZip.setText(""); - edTel1.setText(""); - edTel2.setText(""); - edFax.setText(""); - edFedId.setEditable(true); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/NewOrder.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/NewOrder.java deleted file mode 100644 index c3d60fb749..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/NewOrder.java +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.order; - -import samples.apps.salesplus.*; -import samples.apps.salesplus.db.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * The new order menu. - */ -public class NewOrder extends Container -{ - /** - * Indicates if the order is to be updated or inserted. - */ - static boolean update; - - /** - * An auxiliary order. - */ - static Order currentOrder = new Order(); - - /** - * The product vector. - */ - private Vector products = new Vector(); - - /** - * The deleted products vector. - */ - private Vector vDeleted = new Vector(); - - /** - * The quantity of each product. - */ - private IntVector vQuant = new IntVector(); - - /** - * The order grid. - */ - private Grid grid; - - /** - * The order date edit. - */ - private Edit edDate; - - /** - * The order number edit. - */ - private Edit edNum; - - /** - * The order total edit. - */ - private Edit edTot; - - /** - * A spin list to increase or decrease product quantity. - */ - private SpinList spin; - - /** - * The button to add a product to the order. - */ - private Button btnAdd; - - /** - * The button to remove a product from the order. - */ - private Button btnRemove; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The clear button. - */ - private Button btnClear; - - /** - * The insert or update button. - */ - private Button btnInsert; - - /** - * The customer combo box. - */ - private ComboBox cbCustomer; - - /** - * The product combo box. - */ - private ComboBox cbProduct; - - /** - * Initializes the user interface. - */ - public void initUI() - { - // The container before the grid. - Container container = new Container(); - add(container, LEFT + 2, TOP, FILL - 4, FILL); - container.setBorderStyle(BORDER_RAISED); - container.setBackColor(0xf0f0ff); - - // The order date edit and label. - container.add(edDate = new Edit("XX/XX/XXXX"), RIGHT, TOP + 1); - edDate.setMode(Edit.DATE, true); - SalesPlus.dateAux.setToday(); - edDate.setText(SalesPlus.dateAux.toString()); - container.add(new Label("Date "), BEFORE, SAME); - - // Sets the current order id. - if (currentOrder.orderId == 0) - currentOrder.orderId = OrderDB.getLastOrderId(); - - // The order number label and edit. - container.add(new Label("Order no. "), LEFT + 2, SAME); - container.add(edNum = new Edit("xxx"), AFTER + 20, SAME); - edNum.setText("" + currentOrder.orderId); - edNum.setEditable(false); - - // Customer label and combo box. - container.add(new Label("Customer "), LEFT + 2, AFTER + 5); - container.add(cbCustomer = new ComboBox(), edNum.getRect().x, SAME, FILL, PREFERRED); - container.setRect(LEFT + 2, TOP + 2, FILL - 4, cbCustomer.getRect().y2() + 4); - - // The product label and combo box. - add(new Label("Product "), LEFT + 2, AFTER + 2); - add(cbProduct = new ComboBox(), AFTER, SAME, PREFERRED + Settings.screenWidth / 5, PREFERRED); - - // Quantity label and spin list. - add(new Label("Qty "), AFTER + 3, SAME); - try - { - add(spin = new SpinList(new String[]{"[1,99]"}, false), AFTER, SAME); - } - catch (InvalidNumberException exception) {} - spin.hAlign = CENTER; - - Button.commonGap = 1; - - // The buttons to add or remove a product from the order. - add(btnAdd = new Button(" + "), AFTER + 3, SAME); - add(btnRemove = new Button(" - "), AFTER + 1, SAME); - - // The buttons for insert or update, cancel or back, and clear. - add(btnInsert = new Button("Insert "), RIGHT - 1, BOTTOM - 1); - add(btnBack = new Button("Back"), BEFORE - 2, BOTTOM - 1); - add(btnClear = new Button("Clear"), BEFORE - 2, BOTTOM - 1); - - // The grid. - add(grid = new Grid(new String[]{"Name", "Code", "Qty", "Price"}, - new int[]{fm.stringWidth("xxxxxxxxxx"), fm.stringWidth("xxxxxxxx"), fm.stringWidth("xxx"), fm.stringWidth("xxxxxxx")}, - new int[]{LEFT, LEFT, LEFT, LEFT}, false), LEFT + 5, AFTER + 10, FILL - 5, FIT, btnAdd); - grid.verticalLineStyle = Grid.VERT_DOT; - - // The order total cost edit. - add(edTot = new Edit("xxxxxxx"), LEFT + 2, AFTER); - edTot.setText("0.00"); - edTot.setEditable(false); - - Button.commonGap = 0; - - // Sets some components colors. - edTot.setBackColor(Color.DARK); - grid.setBackColor(Color.WHITE); - edNum.setBackColor(Color.DARK); - container.setBackColor(0xf0f0ff); - - Button.commonGap = 0; - onAddAgain(); - } - - /** - * Sets the new order menu to insert or update an order. - */ - public void onAddAgain() - { - String[] names = CustomerDB.readAllNames(); - - cbCustomer.removeAll(); - - if (names.length == 0) // There are no customer, so there can't add an order. - new MessageBox("Error", "There are no customers registered").popup(); - else - { - cbCustomer.add(names); // Adds all customer names to the combo box. - - cbProduct.removeAll(); - names = ProductDB.readAllNames(); - - if (names.length == 0) // There are no products, so there can't add an order. - new MessageBox("Error", "There are no products registered").popup(); - else - { - cbProduct.add(names); // Adds all product names to the combo box. - - if (update) // The order is being updated. - { - ItemOrder[] items = ItemOrderDB.readAll(currentOrder.orderId); - ItemOrder item; - Product product; - int length = items.length, - i = -1; - - while (++i < length) // Lists all the products and their quantities. - if ((product = ProductDB.getFromCode((item = items[i]).productId)) != null) - { - products.addElement(product); - vQuant.addElement(item.quant); - } - - // Sets the user interface. - cbCustomer.setSelectedItem(currentOrder.customer); - btnInsert.setText("Update"); - edTot.setText(Convert.toString((double)totalValue() * 0.01, 2)); - fillGrid(); - } - else - { - btnInsert.setText("Insert "); - currentOrder.orderId = OrderDB.getLastOrderId(); - } - edNum.setText("" + currentOrder.orderId); - } - } - } - - /** - * Fills the order grid. - */ - private void fillGrid() - { - int count = products.size(), - i = -1; - - grid.removeAllElements(); - if (count == 0) - return; - - String[][] strings = new String[count][4]; - Product product; - - while (++i < count) - { - strings[i][0] = (product = (Product)products.items[i]).name; - strings[i][1] = product.code; - strings[i][2] = Convert.toString(vQuant.items[i]); - - // The price is converted from cents to the right currency. - strings[i][3] = Convert.toString((double)(product.unitPrice * vQuant.items[i]) * 0.01, 2); - } - grid.setItems(strings); - } - - /** - * Calculates the total amount of the order based on the products selected and their quantities. - * - * @return The total amount in cents. - */ - public int totalValue() - { - int total = 0; - int i = products.size(); - - while (--i >= 0) - total += (((Product)products.items[i]).unitPrice * vQuant.items[i]); - - return total; // Result in cents. - } - - /** - * Cleans this menu, clearing the products and quantity vectors, rewinding the grid, reseting the date, selecting index 0 on both customer and - * product combo boxes, and reseting the current order number. - */ - public void clean() - { - // Empties the vectors. - products.removeAllElements(); - vQuant.removeAllElements(); - - grid.removeAllElements(); // Clears the grid. - spin.clear(); // Clears the spin list. - - // Resets the edits. - SalesPlus.dateAux.setToday(); - edDate.setText(SalesPlus.dateAux.toString()); - edTot.setText("0.00"); - - // Sets the combo boxes to select index 0. - cbCustomer.setSelectedIndex(0); - cbProduct.setSelectedIndex(0); - } - - /** - * Gets the selected product. - * - * @return The selected product. - */ - private String[] getSelectedProduct() - { - int idx = grid.getSelectedIndex(); - if (idx < 0) - return null; - return grid.getItem(idx); - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - switch (event.type) - { - case ControlEvent.PRESSED: // A control was pressed. - - if (event.target == btnAdd && cbProduct.getSelectedIndex() >= 0) // Adds a product to the order. - { - int value = spin.getSelectedIndex() + 1; // Gets the selected quantity. - String name = (String)cbProduct.getSelectedItem(); - boolean found = false; - int n = products.size(); - - while (--n >= 0) // Tries to find the product in the list and updates its quantity. - if (((Product)products.items[n]).name.equals(name)) - { - vQuant.items[n] = value; - found = true; - } - - if (!found) // The product was not found, so it will be added to the list. - { - products.addElement(ProductDB.getFromName(name)); - vQuant.addElement(value); - } - - // Updates the total value and the grid. - edTot.setText(Convert.toString((double)totalValue() * 0.01, 2)); - fillGrid(); - } - else if (event.target == btnClear) // Clears the screen. - clean(); - else if (event.target == btnBack) // Goes back to the previous menu. - { - // Empties the vectors. - products.removeAllElements(); - vDeleted.removeAllElements(); - vQuant.removeAllElements(); - - clean(); // Clears the user interface. - - if (update) - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.ORDER_SEARCH]); - else - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.ORDER_MENU]); - - update = false; // The next order won't be updated. - } - else if (event.target == btnRemove) // Removes a product from the order. - { - String[] strings = getSelectedProduct(); - int n = products.size(); - Product product; - - while (--n >= 0) - { - if ((product = (Product)products.items[n]).name.equals(strings[0])) - { - vDeleted.addElement(product); - products.removeElementAt(n); - vQuant.removeElementAt(n); - break; - } - } - edTot.setText(Convert.toString((double)totalValue() * 0.01, 2)); - fillGrid(); - } - else if (event.target == btnInsert) // Inserts an order. This actually creates an order. - { - int n = products.size(); - ItemOrder item; - Product product; - - if (n == 0) // There can't be an order without products. - { - new MessageBox("Error", "There are no products inserted.").popup(); - return; - } - - while (--n >= 0) - { - (item = new ItemOrder()).productId = (product = (Product)products.items[n]).code; - item.unitPrice = product.unitPrice; - item.quant = vQuant.items[n]; - item.orderId = currentOrder.orderId; - item.totalAmount = (item.quant * item.unitPrice); - ItemOrderDB.write(item, update); - } - - if (update) - { - n = vDeleted.size(); - while (--n >= 0) - ItemOrderDB.remove(currentOrder.orderId, ((Product)vDeleted.items[n]).code); - } - - String name = (String)cbCustomer.getSelectedItem(); - if (name.length() != 0) - { - Customer customer = CustomerDB.getFromName(name); - if (customer == null) - return; - - currentOrder.customer = customer.name; - try - { - SalesPlus.dateAux.set(edDate.getText(), Settings.dateFormat); - currentOrder.date = SalesPlus.dateAux.getDateInt(); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - return; - } - currentOrder.totalAmount = totalValue(); - - OrderDB.write(currentOrder, update); - - if (update) - new MessageBox("Message", "Order updated").popup(); - else - new MessageBox("Message", "Order inserted").popup(); - - currentOrder = new Order(); - currentOrder.orderId = OrderDB.getLastOrderId(); - edNum.setText(Convert.toString(currentOrder.orderId)); - - clean(); - if (update) - { - update = false; - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.ORDER_SEARCH]); - } - } - } - - break; - - case ControlEvent.FOCUS_OUT: // Corrects the data inserted to the date format. - - if (event.target == edDate && edDate.getLength() > 0) - try - { - SalesPlus.dateAux.set(edDate.getText(), Settings.dateFormat); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - } - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/OrderMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/OrderMenu.java deleted file mode 100644 index 918a02b5cc..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/OrderMenu.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.order; - -import samples.apps.salesplus.ui.*; -import samples.apps.salesplus.*; - -/** - * The order menu. - */ -public class OrderMenu extends BaseMenu -{ - /** - * The Constructor. - */ - public OrderMenu() - { - super(new String[]{"New Order", "Search"}, new int[]{SalesPlus.NEW_ORDER, SalesPlus.ORDER_SEARCH}, false); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/OrderSearch.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/OrderSearch.java deleted file mode 100644 index fc186b09a9..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/order/OrderSearch.java +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.order; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.gfx.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.util.*; -import totalcross.sys.*; - -/** - * The order search menu. - */ -public class OrderSearch extends Container -{ - /** - * The date edit. - */ - private Edit edDate; - - /** - * The result grid. - */ - private Grid grid; - - /** - * The search button. - */ - private Button btnSearch; - - /** - * The back button; - */ - private Button btnBack; - - /** - * The view button. - */ - private Button btnView; - - /** - * The remove button. - */ - private Button btnRemove; - - /** - * The customer combo box. - */ - private ComboBox cbCustomer; - - /** - * Initializes the user interface. - */ - public void initUI() - { - Button.commonGap = 1; - - // Customer label and combo box. - add(new Label("Customer: "), LEFT + 2, TOP + 2); - add(cbCustomer = new ComboBox(), AFTER, SAME, FILL, PREFERRED); - - // Date label and edit. - add(new Label("Date: "), LEFT + 2, AFTER + 2); - add(edDate = new Edit("XX/XX/XXXX"), cbCustomer.getRect().x, SAME); - edDate.setMode(Edit.DATE, true); - - // The buttons. - add(btnSearch = new Button("Search"), AFTER + 2, SAME); - add(btnView = new Button("View"), RIGHT - 1, BOTTOM - 1); - add(btnBack = new Button("Back"), BEFORE - 2, SAME); - add(btnRemove = new Button("Remove"), BEFORE - 2, SAME); - - // The grid. - add(grid = new Grid(new String[]{"ID", "Date", "Customer", "Total Amount"}, - new int[]{fm.stringWidth("xx"), fm.stringWidth("XX/XX/XXXX"), fm.stringWidth("name of the customer"), fm.stringWidth("123456789")}, - new int[]{LEFT, CENTER, LEFT, RIGHT}, false), LEFT + 5, AFTER + 7, FILL - 5, FIT, btnSearch); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - // Loads the customer names in the combo box. - String[] names = CustomerDB.readAllNames(); - if (names.length == 0) - new MessageBox("Error", "There are no customers registered").popup(); - else - cbCustomer.add(names); - } - - /** - * Updates the grid. - */ - public void onAddAgain() - { - String nameStr = (String)cbCustomer.getSelectedItem(); - String dateStr = edDate.getText(); - Order[] orders = null; - - try // Tries to find the desired order. - { - orders = OrderDB.search(nameStr, dateStr); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - return; - } - - int count = orders.length; - - if (count == 0) // No order found: empties the grid. - grid.removeAllElements(); - else // Fills the grid with the orders found. - { - String[][] strings = new String[count][4]; - int i = -1; - - while (++i < count) - { - strings[i][0] = Convert.toString(orders[i].orderId); - strings[i][1] = dateStr; - strings[i][2] = nameStr; - strings[i][3] = Convert.toString((double)orders[i].totalAmount * 0.01, 2); - } - grid.setItems(strings); - } - } - - /** - * Returns the selected order in the grid. - * - * @return The selected order. - */ - private Order getSelectedOrder() - { - int idx = grid.getSelectedIndex(); - - if (idx < 0) - return null; - String[] strings = grid.getItem(idx); - - try - { - return OrderDB.getFromId(Convert.toInt(strings[0])); - } - catch (InvalidNumberException exception) - { - return null; // Never occurs. - } - } - - /** - * Called to the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - switch (event.type) - { - case ControlEvent.PRESSED: - if (event.target == btnBack) // Back button: goes back to the previous menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.ORDER_MENU]); - else if (event.target == btnSearch) // Search button: updates the grid with the results. - onAddAgain(); - else if (event.target == btnView) // View button: views more details of the selected order. - { - Order order = getSelectedOrder(); - - if (order != null) // If there is a selected order, views it in detail. - { - NewOrder.currentOrder = order; - NewOrder.update = true; - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.NEW_ORDER]); - } - } - else if (event.target == btnRemove) // Remove button: removes the selected order. - { - Order order = getSelectedOrder(); - if (order != null) - { - OrderDB.removeOrder(order); - ItemOrderDB.removeAll(order.orderId); - onAddAgain(); - } - } - break; - - case ControlEvent.FOCUS_OUT: // Corrects the data inserted to the date format. - if (event.target == edDate && edDate.getLength() > 0) - { - try - { - SalesPlus.dateAux.set(edDate.getText(), Settings.dateFormat); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - } - } - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/NewProduct.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/NewProduct.java deleted file mode 100644 index 05acc37959..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/NewProduct.java +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.product; - -import litebase.PrimaryKeyViolationException; -import samples.apps.salesplus.*; -import samples.apps.salesplus.db.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.util.BigDecimal; -import totalcross.sys.*; - -/** - * The new product menu. - */ -public class NewProduct extends Container -{ - /** - * An auxiliary product. - */ - public static Product sentProduct; - - /** - * The insert / update button. - */ - private Button btnInsert; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The clear button. - */ - private Button btnClear; - - /** - * The name edit. - */ - private Edit edName; - - /** - * The code edit. - */ - private Edit edCode; - - /** - * The description edit. - */ - private Edit edDescr; - - /** - * The price edit. - */ - private Edit edPrice; - - /** - * Initializes the user interface. - */ - public void initUI() - { - Button.commonGap = 1; - - // Name label and edit. - add(new Label("Name: "), LEFT + 5, TOP + 5); - add(edName = new Edit(""), AFTER + 20, SAME, PREFERRED - 5, PREFERRED); - - int initX = edName.getRect().x; - - // Code label and edit. - add(new Label("Code: "), LEFT + 5, AFTER + 2); - add(edCode = new Edit(""), initX, SAME); - - // Product description. - add(new Label("Descrip.: "), LEFT + 5, AFTER + 2); - add(edDescr = new Edit(""), initX, SAME); - - // Price label and edit. - add(new Label("Price: "), LEFT + 5, AFTER + 2); - add(edPrice = new Edit(""), initX, SAME); - edPrice.setMode(Edit.CURRENCY, true); - - // Buttons. - add(btnInsert = new Button("Insert "), RIGHT - 1, BOTTOM - 1); - add(btnBack = new Button("Back"), BEFORE - 2, SAME); - add(btnClear = new Button("Clear"), BEFORE - 2, SAME); - - onAddAgain(); - } - - /** - * Sets the new product menu to insert or update a product. - */ - public void onAddAgain() - { - if (sentProduct == null) // No selected product: a new one will be inserted. - { - clearEdit(); - btnInsert.setText("Insert"); - } - else // Updates the selected product. - { - edName.setText(sentProduct.name); - edCode.setText(sentProduct.code); - edDescr.setText(sentProduct.descr); - edPrice.setText(Convert.toString((double)sentProduct.unitPrice * 0.01, 2)); - edCode.setEditable(false); - btnInsert.setText("Update"); - } - } - - /** - * Called to process posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnInsert) // The insert button. - { - Product product = new Product(); - product.name = edName.getText(); - product.code = edCode.getText(); - product.descr = edDescr.getText(); - - // Checks if the price is valid. - String string = edPrice.getTextWithoutMask(); - if (string.length() != 0) - try - { - product.unitPrice = Convert.toInt(new BigDecimal(string).movePointRight(2).toString()); - } - catch (InvalidNumberException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - return; - } - else - product.unitPrice = -1; - - StringBuffer sBuffer = SalesPlus.sBuffer; - sBuffer.setLength(0); - - // Checks that all the fields have data. - if (product.name.length() == 0) - sBuffer.append(" Product name|"); - if (product.code.length() == 0) - sBuffer.append(" Product code|"); - if (product.descr.length() == 0) - sBuffer.append(" Product description|"); - if (product.unitPrice == -1) - sBuffer.append(" Unit Price"); - - if (sBuffer.length() > 0) - { - new MessageBox("Error","Please fill in the following fields:|" + sBuffer.toString()).popup(); - return; - } - - try - { - if (ProductDB.write(product, sentProduct != null) == 0) // Inserts or updates product. - { - new MessageBox("Error", "Product name can't be repeated.").popup(); - return; - } - } - catch (PrimaryKeyViolationException exception) - { - new MessageBox("Error", "Product code already exists").popup(); - return; - } - - if (sentProduct != null) // Updated the product. - { - sentProduct = null; - new MessageBox("Message", "Product updated").popup(); - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.PRODUCT_SEARCH]); - } - else // Inserted a new product. - new MessageBox("Message", "Product inserted").popupNonBlocking(); - - clearEdit(); - } - else if (event.target == btnClear) // Clear button: clears the edits. - clearEdit(); - - if (event.target == btnBack) // Back button. - { - if (sentProduct != null) // Goes to the search screen if a product was being updated. - { - sentProduct = null; - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.PRODUCT_SEARCH]); - } - else // Goes to the product menu if a product was being inserted. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.PRODUCT_MENU]); - } - } - } - - /** - * Clears all the edits. - */ - public void clearEdit() - { - edName.setText(""); - edCode.setText(""); - edDescr.setText(""); - edPrice.setText(""); - edCode.setEditable(true); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/ProductMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/ProductMenu.java deleted file mode 100644 index 9499ebe78e..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/ProductMenu.java +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.product; - -import samples.apps.salesplus.ui.*; -import samples.apps.salesplus.*; - -public class ProductMenu extends BaseMenu -{ - /** - * The Constructor. - */ - public ProductMenu() - { - super(new String[]{"New Product", "Search"}, new int[]{SalesPlus.NEW_PRODUCT, SalesPlus.PRODUCT_SEARCH}, false); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/ProductSearch.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/ProductSearch.java deleted file mode 100644 index 45f4885953..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/product/ProductSearch.java +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.product; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; - -/** - * The product search menu. - */ -public class ProductSearch extends Container -{ - /** - * The back button. - */ - private Button btnBack; - - /** - * The edit button. - */ - private Button btnEdit; - - /** - * The remove button. - */ - private Button btnRemove; - - /** - * The search button. - */ - private Button btnSearch; - - /** - * The grid. - */ - private Grid grid; - - /** - * The name edit. - */ - private Edit edName; - - /** - * The code edit. - */ - private Edit edCode; - - /** - * Initializes the user interface. - */ - public void initUI() - { - Button.commonGap = 1; - - // Name label and edit. - add(new Label("Name: "), LEFT + 5, TOP + 2); - add(edName = new Edit(""), AFTER + 5, SAME, FILL - 5, PREFERRED); - - // Code label and edit. - add(new Label("Code: "), LEFT + 5, AFTER + 2); - add(edCode = new Edit(""), edName.getRect().x, SAME, FILL - 5, PREFERRED); - - // The buttons. - add(btnSearch = new Button("Search"), RIGHT - 5, AFTER + 2); - add(btnEdit = new Button("View"), RIGHT - 1, BOTTOM - 1); - add(btnBack = new Button("Back"), BEFORE - 2, SAME); - add(btnRemove = new Button("Remove"), BEFORE - 2, SAME); - - // The grid. - add(grid = new Grid(new String[]{"Name", "Code", "Descr", "Price"}, new int[]{fm.stringWidth("xxxxxxxxxxxxxxxx"), fm.stringWidth("x"), fm.stringWidth("xxxxxxxxxxxxxxxxx"), fm.stringWidth("x")}, new int[]{LEFT, LEFT, LEFT, RIGHT}, false)); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - grid.setRect(LEFT + 5, AFTER + 5, FILL - 5, FIT, btnSearch); - } - - /** - * Updates the grid searching the product again. - */ - public void onAddAgain() - { - String nameStr = edName.getText(); - String codeStr = edCode.getText(); - Product[] products = ProductDB.search(nameStr, codeStr); - int count = products.length; - - if (count == 0) // No elements found: erases the grid. - grid.removeAllElements(); - else // Loads the grid. - { - String[][] strings = new String[count][4]; - int i = -1; - - while (++i < count) - { - strings[i][0] = nameStr; - strings[i][1] = codeStr; - strings[i][2] = products[i].descr; - strings[i][3] = Convert.toString((double)products[i].unitPrice * 0.01, 2); - } - grid.setItems(strings); - } - } - - /** - * Returns the selected product in the grid. - * - * @return The selected product or null if none was selected. - */ - private Product getSelectedProduct() - { - int idx = grid.getSelectedIndex(); - if (idx < 0) - return null; - return ProductDB.getFromCode(grid.getItem(idx)[1]); - } - - /** - * Called to the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnSearch) // Search button: reloads the grid. - onAddAgain(); - else if (event.target == btnBack) // Back button: goes back to the product menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.PRODUCT_MENU]); - else if (event.target == btnEdit) // Edit button: goes to the new product menu to update the selected product. - { - Product product = getSelectedProduct(); - if (product != null) - { - NewProduct.sentProduct = product; - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.NEW_PRODUCT]); - } - } - else if (event.target == btnRemove) // Remove button: removes the selected product. - { - Product product = getSelectedProduct(); - if (product != null) // must always be true - { - if (!ItemOrderDB.hasProduct(product)) - { - ProductDB.remove(product); - onAddAgain(); - } - else - new MessageBox("Error", "There are orders that belong to | this product. If you really want " - + "| to remove it, delete the orders first.").popup(); - } - } - - } - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/ReportMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/ReportMenu.java deleted file mode 100644 index a1ccbbfb79..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/ReportMenu.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report; - -import samples.apps.salesplus.ui.*; -import samples.apps.salesplus.*; - -/** - * The report menu. - */ -public class ReportMenu extends BaseMenu -{ - /** - * The constructor. - */ - public ReportMenu() - { - super(new String[]{"Summary", "By Product"}, new int[]{SalesPlus.SUMMARY_MENU, SalesPlus.BY_PRODUCT_MENU}, false); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/ByProductMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/ByProductMenu.java deleted file mode 100644 index 0e3a048f58..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/ByProductMenu.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.byproduct; - -import samples.apps.salesplus.*; -import samples.apps.salesplus.ui.*; - -/** - * The report menu by product. - */ -public class ByProductMenu extends BaseMenu -{ - /** - * The constructor. - */ - public ByProductMenu() - { - super(new String[]{"Month", "Day", "Period"}, new int[]{SalesPlus.MONTH2, SalesPlus.DAY2, SalesPlus.PERIOD}, false); - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Day2.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Day2.java deleted file mode 100644 index f7b768db1e..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Day2.java +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.byproduct; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.dialog.MessageBox; -import totalcross.ui.event.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * The report by product and day. - */ -public class Day2 extends Container -{ - /** - * The Ok button. - */ - private Button btnOk; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The date edit. - */ - private Edit edDate; - - /** - * The total edit. - */ - private Edit edTotal; - - /** - * The result grid. - */ - private Grid grid; - - /** - * Initializes the user interface. - */ - public void initUI() - { - // Date label and edit. - add(new Label("Date: "), LEFT + 5, TOP + 5); - add(edDate = new Edit("XX/XX/XXXX"), AFTER, SAME); - edDate.setMode(Edit.DATE, true); - edDate.setText(SalesPlus.dateAux.toString()); - - // Buttons. - add(btnOk = new Button("Ok"), AFTER + 5, SAME); - add(btnBack = new Button("Back"), RIGHT - 2, BOTTOM - 1); - - // The grid. - add(grid = new Grid(new String[]{"Product", "Qty", "Amount"}, - new int[]{fm.stringWidth("xxxxxxxxx"), fm.stringWidth("xxxx"), fm.stringWidth("xxxxxxxx")}, - new int[]{LEFT, CENTER, RIGHT}, false), LEFT + 5, AFTER + 5, FILL - 5, FIT, btnOk); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - // Total label and edit. - add(new Label("Total: "), LEFT + 5, AFTER); - add(edTotal = new Edit("xxxxxxx"), AFTER + 2, SAME); - edTotal.setEditable(false); - edTotal.alignment = RIGHT; - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - switch (event.type) - { - case ControlEvent.PRESSED: - if (event.target == btnOk) // Ok button: does the search. - { - Date date = SalesPlus.dateAux; - try - { - date.set(edDate.getText(), Settings.dateFormat); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - return; - } - - Order[] orders = OrderDB.getFromDate(date.getDateInt()); - int count1 = orders.length; - - grid.removeAllElements(); - if (count1 > 0) - { - String[][][] strings = new String[count1][][]; - ItemOrder[] items; - ItemOrder item; - int i1 = -1, - total = 0, - i2, - count2; - - while (++i1 < count1) - { - strings[i1] = new String[count2 = (items = ItemOrderDB.readAll(orders[i1].orderId)).length][3]; - i2 = -1; - - while (++i2 < count2) - { - strings[i1][i2][0] = ProductDB.getFromCode((item = items[i2]).productId).name; - strings[i1][i2][1] = Convert.toString(item.quant); - strings[i1][i2][2] = Convert.toString((double)item.totalAmount * 0.01, 2); - total = total + item.totalAmount; - } - grid.add(strings[i1]); - } - - edTotal.setText(Convert.toString((double)total * 0.01, 2)); - } - } - else if (event.target == btnBack) // Back button: goes back to the previous menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.BY_PRODUCT_MENU]); - break; - - case ControlEvent.FOCUS_OUT: // Corrects the data inserted to the date format. - - if (event.target == edDate && edDate.getLength() > 0) - try - { - SalesPlus.dateAux.set(edDate.getText(), Settings.dateFormat); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - } - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Month2.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Month2.java deleted file mode 100644 index 65b32f5b23..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Month2.java +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.byproduct; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * The report by product and month. - */ -public class Month2 extends Container -{ - /** - * The Ok button. - */ - private Button btnOk; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The total edit. - */ - private Edit edTotal; - - /** - * The combo box for years. - */ - private ComboBox combYear; - - /** - * The combo box for months. - */ - private ComboBox combMonth; - - /** - * The result grid. - */ - private Grid grid; - - /** - * Initializes the user interface. - */ - public void initUI() - { - SalesPlus.dateAux.setToday(); - - // Year label and combo box. - add(new Label("Year "), LEFT + 5, TOP + 5); - add(combYear = new ComboBox(SalesPlus.years), AFTER, SAME); - combYear.setSelectedItem(Convert.toString(SalesPlus.dateAux.getYear())); - - // Month label and combo box. - add(new Label("Month "), AFTER + 5, SAME); - add(combMonth = new ComboBox(Date.monthNames), AFTER, SAME); - combMonth.setSelectedIndex(SalesPlus.dateAux.getMonth()); - - // Buttons. - add(btnOk = new Button("Ok"), AFTER + 5, SAME); - add(btnBack = new Button("Back"), RIGHT - 2, BOTTOM - 1); - - // The grid. - add(grid = new Grid(new String[]{"Product", "Qty", "Amount"}, - new int[]{fm.stringWidth("xxxxxxxxx"), fm.stringWidth("xxxx"), fm.stringWidth("xxxxxxxx")}, - new int[]{LEFT, CENTER, RIGHT}, false), LEFT + 5, AFTER + 5, FILL - 5, FIT, btnOk); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - // Total label and edit. - add(new Label("Total: "), LEFT + 5, AFTER); - add(edTotal = new Edit("xxxxxxx"), AFTER + 2, SAME); - edTotal.setEditable(false); - edTotal.alignment = RIGHT; - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnOk) // Ok button: does the search. - { - Order[] orders = OrderDB.readAllDays(combYear.getSelectedIndex() + 2005, combMonth.getSelectedIndex()); - int count1 = orders.length; - - grid.removeAllElements(); - if (count1 > 0) - { - String[][][] strings = new String[count1][][]; - ItemOrder[] items; - ItemOrder item; - int i1 = -1, - total = 0, - i2, - count2; - - while (++i1 < count1) - { - strings[i1] = new String[count2 = (items = ItemOrderDB.readAll(orders[i1].orderId)).length][3]; - i2 = -1; - - while (++i2 < count2) - { - strings[i1][i2][0] = ProductDB.getFromCode((item = items[i2]).productId).name; - strings[i1][i2][1] = Convert.toString(item.quant); - strings[i1][i2][2] = Convert.toString((double)item.totalAmount * 0.01, 2); - total = total + item.totalAmount; - } - grid.add(strings[i1]); - } - - edTotal.setText(Convert.toString((double)total * 0.01, 2)); - } - } - else if (event.target == btnBack) // Back button: goes back to the previous menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.BY_PRODUCT_MENU]); - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Period.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Period.java deleted file mode 100644 index 1cbb71ec6c..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/byproduct/Period.java +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.byproduct; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.dialog.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * The report by product and period. - */ -public class Period extends Container -{ - /** - * The Ok button. - */ - private Button btnOk; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The first date edit. - */ - private Edit edDateFrom; - - /** - * The last date edit. - */ - private Edit edDateTo; - - /** - * The total edit. - */ - private Edit edTotal; - - /** - * The result grid. - */ - private Grid grid; - - /** - * Initializes the user interface. - */ - public void initUI() - { - // Set today date. - Date date = SalesPlus.dateAux; - date.setToday(); - - // Initial date label and edit. - add(new Label("From "), LEFT + 5, TOP + 5); - add(edDateFrom = new Edit("XX/XX/XXXX"), AFTER, SAME); - edDateFrom.setMode(Edit.DATE, true); - edDateFrom.setText(date.toString()); - - // Final date label and edit. - add(new Label("To "), LEFT + 5, AFTER + 2); - add(edDateTo = new Edit("XX/XX/XXXX"), edDateFrom.getRect().x, SAME); - edDateTo.setMode(Edit.DATE, true); - edDateTo.setText(date.toString()); - - // Buttons. - add(btnOk = new Button("Ok"), AFTER + 5, SAME); - add(btnBack = new Button("Back"), RIGHT - 2, BOTTOM - 1); - - // The grid. - add(grid = new Grid(new String[]{"Product", "Qty", "Amount"}, - new int[]{fm.stringWidth("xxxxxxxxx"), fm.stringWidth("xxxx"), fm.stringWidth("xxxxxxxx")}, - new int[]{LEFT, CENTER, RIGHT}, false), LEFT + 5, AFTER + 5, FILL - 5, FIT, btnOk); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - // Total label and edit. - add(new Label("Total: "), LEFT + 5, AFTER); - add(edTotal = new Edit("xxxxxxx"), AFTER + 2, SAME); - edTotal.setEditable(false); - edTotal.alignment = RIGHT; - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - switch (event.type) - { - case ControlEvent.PRESSED: - if (event.target == btnOk) // Ok button: does the search. - { - int date1, - date2; - Date date = SalesPlus.dateAux; - - try - { - date.set(edDateTo.getText(), Settings.dateFormat); - date2 = date.getDateInt(); - date.set(edDateFrom.getText(), Settings.dateFormat); - date1 = date.getDateInt(); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - return; - } - - if (date1 > date2) - { - new MessageBox("Message", "ATTENTION | The date 'to' can | be not lower than | the date 'from'").popup(); - return; - } - - Vector vector = new Vector(1); - Object[] orders; - - while (date1 <= date2) - { - orders = OrderDB.getFromDate(date1); - if (orders.length != 0) - vector.addElements(orders); - date.advance(1); - date1 = date.getDateInt(); - } - - orders = vector.items; - int count1 = vector.size(); - - grid.removeAllElements(); - if (count1 > 0) - { - String[][][] strings = new String[count1][][]; - ItemOrder[] items; - ItemOrder item; - int i1 = -1, - total = 0, - i2, - count2; - - while (++i1 < count1) - { - strings[i1] = new String[count2 = (items = ItemOrderDB.readAll(((Order)orders[i1]).orderId)).length][3]; - i2 = -1; - - while (++i2 < count2) - { - strings[i1][i2][0] = ProductDB.getFromCode((item = items[i2]).productId).name; - strings[i1][i2][1] = Convert.toString(item.quant); - strings[i1][i2][2] = Convert.toString((double)item.totalAmount * 0.01, 2); - total = total + item.totalAmount; - } - grid.add(strings[i1]); - } - - edTotal.setText(Convert.toString((double)total * 0.01, 2)); - } - } - else if (event.target == btnBack) - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.BY_PRODUCT_MENU]); - break; - - case ControlEvent.FOCUS_OUT: // Corrects the data inserted to the date format. - try - { - if (event.target == edDateFrom && edDateFrom.getLength() > 0) - { - SalesPlus.dateAux.set(edDateFrom.getText(), Settings.dateFormat); - edDateFrom.setText(SalesPlus.dateAux.toString()); - } - else if (event.target == edDateTo && edDateTo.getLength() > 0) - { - SalesPlus.dateAux.set(edDateTo.getText(), Settings.dateFormat); - edDateTo.setText(SalesPlus.dateAux.toString()); - } - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - } - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/Day.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/Day.java deleted file mode 100644 index ab56f739a0..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/Day.java +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.summary; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.dialog.MessageBox; -import totalcross.ui.event.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * The report by summary and day. - */ -public class Day extends Container -{ - /** - * The Ok button. - */ - private Button btnOk; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The date edit. - */ - private Edit edDate; - - /** - * The total edit. - */ - private Edit edTotal; - - /** - * The result grid. - */ - private Grid grid; - - /** - * Initializes the user interface. - */ - public void initUI() - { - // Date label and edit. - add(new Label("Date: "), LEFT + 5, TOP + 5); - add(edDate = new Edit("XX/XX/XXXX"), AFTER, SAME); - edDate.setMode(Edit.DATE, true); - edDate.setText(SalesPlus.dateAux.toString()); - - // Buttons. - add(btnOk = new Button("Ok"), AFTER + 5, SAME); - add(btnBack = new Button("Back"), RIGHT - 2, BOTTOM - 1); - - // The grid. - add(grid = new Grid(new String[]{"ID", "Customer", "Amount"}, - new int[]{fm.stringWidth("x"), fm.stringWidth("xxxxxxxxxxxxxxxx"), fm.stringWidth("x")}, - new int[]{LEFT, LEFT, RIGHT}, false), LEFT + 5, AFTER + 5, FILL - 5, FIT, btnOk); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - // Total label and edit. - add(new Label("Total: "), LEFT + 5, AFTER); - add(edTotal = new Edit("xxxxxxx"), AFTER + 2, SAME); - edTotal.setEditable(false); - edTotal.alignment = RIGHT; - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - switch (event.type) - { - case ControlEvent.PRESSED: - if (event.target == btnOk) - { - Date date = SalesPlus.dateAux; - try - { - date.set(edDate.getText(), Settings.dateFormat); - } - catch (InvalidDateException exception) - { - new MessageBox("Error", exception.getMessage()).popup(); - return; - } - - int total = 0; - Order[] orders = OrderDB.getFromDate(date.getDateInt()); - int count = orders.length; - - grid.removeAllElements(); - if (count > 0) - { - String[][] strings = new String[count][3]; - Order order; - int i = -1; - - while (++i < count) - { - total = total + (order = orders[i]).totalAmount; - strings[i][0] = Convert.toString(order.orderId); - strings[i][1] = order.customer; - strings[i][2] = Convert.toString((double)order.totalAmount * 0.01, 2); - } - - grid.setItems(strings); - edTotal.setText(Convert.toString((double)total * 0.01, 2)); - } - } - else if (event.target == btnBack) - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.SUMMARY_MENU]); - - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/Month.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/Month.java deleted file mode 100644 index df19c064aa..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/Month.java +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.summary; - -import samples.apps.salesplus.db.*; -import samples.apps.salesplus.*; -import totalcross.ui.*; -import totalcross.ui.event.*; -import totalcross.ui.gfx.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * The report by summary and month. - */ -public class Month extends Container -{ - /** - * The Ok button. - */ - private Button btnOk; - - /** - * The back button. - */ - private Button btnBack; - - /** - * The total edit. - */ - private Edit edTotal; - - /** - * The combo box for years. - */ - private ComboBox combYear; - - /** - * The combo box for months. - */ - private ComboBox combMonth; - - /** - * The result grid. - */ - private Grid grid; - - /** - * Initializes the user interface. - */ - public void initUI() - { - SalesPlus.dateAux.setToday(); - - // Year label and combo box. - add(new Label("Year "), LEFT + 5, TOP + 5); - add(combYear = new ComboBox(SalesPlus.years), AFTER, SAME); - combYear.setSelectedItem(Convert.toString(SalesPlus.dateAux.getYear())); - - // Month label and combo box. - add(new Label("Month "), AFTER + 5, SAME); - add(combMonth = new ComboBox(Date.monthNames), AFTER, SAME); - combMonth.setSelectedIndex(SalesPlus.dateAux.getMonth()); - - // Buttons. - add(btnOk = new Button("Ok"), AFTER + 5, SAME); - add(btnBack = new Button("Back"), RIGHT - 2, BOTTOM - 1); - - // The grid. - add(grid = new Grid(new String[]{"Day", "Amount"}, new int[]{fm.stringWidth("xxxxxx"), fm.stringWidth("xxxxxxxxxx")}, - new int[]{CENTER, RIGHT}, false), LEFT + 5, AFTER + 5, FILL - 5, FIT, btnOk); - grid.verticalLineStyle = Grid.VERT_DOT; - grid.setBackColor(Color.WHITE); - - // Total label and edit. - add(new Label("Total: "), LEFT + 5, AFTER); - add(edTotal = new Edit("xxxxxxx"), AFTER + 2, SAME); - edTotal.setEditable(false); - edTotal.alignment = RIGHT; - } - - /** - * Called to process the posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btnOk) - { - Order[] orders = OrderDB.readAllDays(combYear.getSelectedIndex() + 2005, combMonth.getSelectedIndex()); - int count = orders.length; - - grid.removeAllElements(); - if (count > 0) - { - String[][] strings = new String[count][2]; - Order order; - int i = -1, - total = 0; - - while (++i < count) - { - total = total + (order = orders[i]).totalAmount; - strings[i][0] = Convert.toString(order.date % 100); - strings[i][1] = Convert.toString((double)order.totalAmount * 0.01, 2); - } - - grid.setItems(strings); - edTotal.setText(Convert.toString((double)total * 0.01, 2)); - } - } - else if (event.target == btnBack) // Back button: goes back to the previous menu. - MainWindow.getMainWindow().swap(SalesPlus.screens[SalesPlus.BY_PRODUCT_MENU]); - } - } -} diff --git a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/SummaryMenu.java b/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/SummaryMenu.java deleted file mode 100644 index 6d2f863bb2..0000000000 --- a/LitebaseSDK/src/java/samples/apps/salesplus/ui/report/summary/SummaryMenu.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.salesplus.ui.report.summary; - -import samples.apps.salesplus.ui.*; -import samples.apps.salesplus.*; - -/** - * The report summary menu. - */ -public class SummaryMenu extends BaseMenu -{ - /** - * The constructor. - */ - public SummaryMenu() - { - super(new String[]{"Day", "Month"}, new int[]{SalesPlus.DAY, SalesPlus.MONTH}, false); - } - -} diff --git a/LitebaseSDK/src/java/samples/apps/sqlconsole/SQLConsole.java b/LitebaseSDK/src/java/samples/apps/sqlconsole/SQLConsole.java deleted file mode 100644 index 5761ee5b5e..0000000000 --- a/LitebaseSDK/src/java/samples/apps/sqlconsole/SQLConsole.java +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.apps.sqlconsole; - -import litebase.*; - -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.ui.*; -import totalcross.ui.dialog.*; -import totalcross.ui.event.*; -import totalcross.ui.font.*; -import totalcross.ui.gfx.*; -import totalcross.ui.image.*; -import totalcross.util.*; - -/** - * A SQL console application for Litebase. - */ -public class SQLConsole extends MainWindow -{ - /** - * The possible operands. - */ - private static final String[] OPERS = Settings.screenWidth == 160? - new String[] {"!=", "=", "<", ">", "(", ")", ",", "*", "_", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} - : new String[] {"!=", "=", "<", ">", "<=", ">=", "(", ")", ",", "*", "_", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - - /** - * One of the color used. - */ - static final int COLOR_1 = 0xA0C0FF; - - /** - * Another color. - */ - static final int COLOR_2 = 0xBEDEFF; - - /** - * The menu bar. - */ - private MenuBar menuBar; - - /** - * The multi edit. - */ - private MultiEdit multiEdit; - - /** - * Execute button. - */ - private Button btnExe; - - /** - * Clear button. - */ - private Button btnClr; - - /** - * The result grid. - */ - private Grid grid; - - /** - * The status label. - */ - private Label lStatus; - - /** - * The tabbed container to alternate from the query container to the results container. - */ - private TabbedContainer tabCont; - - /** - * The combo box of old sql queries. - */ - private ComboBox cbsql; - - /** - * The sql command key words. - */ - private PushButtonGroup pbgcmd; - - /** - * SQL operators. - */ - private PushButtonGroup pbgoper; - - /** - * The possible letters present in a query. - */ - private PushButtonGroup pbgletters; - - /** - * The key event. - */ - private KeyEvent keyEvent = new KeyEvent(); - - /** - * A button to add a query to the combo box. - */ - private Button btAdd; - - /** - * A button to remove a query to the combo box. - */ - private Button btRem; - - private Button btCopyResults; - - private MenuItem miAscii; // guich@251_2: SQLConsole can now be used with ascii tables. - private MenuItem miCrypto; - - /** - * The id of the database used in the current session. - */ - private String databaseId; - - /** - * The connection with Litebase. - */ - private LitebaseConnection conn; - - /** - * The time a query takes. - */ - private int time; - - static - { - Settings.useNewFont = true; - } - - /** - * The constructor. - */ - public SQLConsole() - { - if (Settings.screenWidth > 400) - setDefaultFont(Font.getFont(false, 14)); - setUIStyle(Settings.Vista); - Grid.useHorizontalScrollBar = true; - - } - - /** - * Initializes the user interface. - */ - public void initUI() - { - try - { - Vm.setAutoOff(false); // The device won't turn off the screen. - - // The menu. - MenuItem[] items = - { - new MenuItem("File"), - new MenuItem("Change app id"), - new MenuItem("Use default app id"), - new MenuItem(), - miAscii = new MenuItem("Is ascii", false), // guich@251_2: SQLConsole can now be used with ascii tables. - miCrypto = new MenuItem("Is Crypto", false), - new MenuItem(), - new MenuItem("Exit") - }; - setMenuBar(menuBar = new MenuBar(new MenuItem[][]{items})); - - // guich@251_2: SQLConsole can now be used with ascii tables. - // retrieve data from applicationid - String s = Settings.appSecretKey; - if (s != null) - { - if (s.indexOf('|') == -1) // legacy - databaseId = s; - else - { - databaseId = s.substring(0,4); - miAscii.isChecked = s.charAt(5) == '1'; - miCrypto.isChecked = s.length() == 7 && s.charAt(6) == '1'; - } - } - else databaseId = Settings.applicationId; - connChanged(); - - Container bottomBar = new Container(); - add(bottomBar, LEFT, BOTTOM, FILL, (int) (fmH * 1.25)); - bottomBar.add(btCopyResults = new Button("Copy to transfer area"), RIGHT, BOTTOM); - - Button.commonGap = 1; // The button gaps will be the same. - int blue = Color.getRGB(173, 214, 255); // Gets the color. - bottomBar.add(lStatus = new Label(), LEFT, BOTTOM, FIT, PREFERRED); // Status label. - - // The tabs with SQL queries and queries result. - add(tabCont = new TabbedContainer(new String[]{"SQL", "Results"})); - tabCont.setRect(LEFT, TOP, FILL, FIT, bottomBar); - tabCont.setBackColor(blue); - - // SQL combo box. - Container container = tabCont.getContainer(0); - container.setBackColor(blue); - cbsql = new ComboBox(); - container.add(btAdd = new Button("+"), RIGHT, TOP, PREFERRED, cbsql.getPreferredHeight()); - container.add(btRem = new Button("-"), BEFORE - 2, TOP, SAME, SAME); - int color = Color.getRGB(50, 203, 255); - btAdd.setBackColor(color); - btRem.setBackColor(color); - container.add(cbsql); - cbsql.setBackColor(Color.darker(color)); - cbsql.setRect(LEFT, TOP, FIT - 2, PREFERRED); - cbsql.enableHorizontalScroll(); - cbsql.fullWidth = true; - - // The buttons that execute and clears the queries. - container.add(btnExe = new Button(new Image("go.gif").smoothScaledFromResolution(320))); - btnExe.setBorder(Button.BORDER_NONE); - btnExe.setRect(RIGHT, AFTER + 1, PREFERRED, PREFERRED, btRem); - container.add(btnClr = new Button(new Image("clear.gif").smoothScaledFromResolution(320))); - btnClr.setBorder(Button.BORDER_NONE); - btnClr.setRect(RIGHT, AFTER, PREFERRED, PREFERRED); - - // The multi edit. - boolean tall = Settings.screenHeight > Settings.screenWidth; - container.add(multiEdit = new MultiEdit(tall? 7 : 3,3)); - multiEdit.setBackColor(COLOR_1); - multiEdit.setRect(LEFT, AFTER + 1, FIT - 1, PREFERRED, cbsql); - - // The buttons with commands, operators and letters. - container.add(pbgcmd = new PushButtonGroup(new String[] - {"select", "from", "where", "create", "like", "table", "and", "or", "insert into", "values", "update", "delete", "drop", "index", "count"}, - false, -1, 0, 8, 3, false, PushButtonGroup.BUTTON),LEFT + 1,AFTER + 1); - pbgcmd.setFocusLess(true); - container.add(pbgoper = new PushButtonGroup(OPERS, 0, 2),LEFT + 1,AFTER + 1); - pbgoper.setFocusLess(true); - if (tall) - { - container.add(pbgletters = new PushButtonGroup(new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", null, "�", - "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", null, " "}, 0, 2),LEFT + 1,AFTER + 1); - pbgletters.setFocusLess(true); - } - pbgoper.setBackColor(color); - - if (Settings.appSettings != null) // Gets the last query and displays it. - multiEdit.setText(Settings.appSettings); - if (Settings.appSettingsBin != null) // Populates the combobox query. - { - DataStream ds = new DataStream(new ByteArrayStream(Settings.appSettingsBin)); - cbsql.add(ds.readStringArray()); - } - cbsql.setSelectedIndex(0); - addGrid(new String[]{"No queries executed."}); // No queries were executed yet. - lStatus.setText("Current database id: " + databaseId); // The current connection id. - } - catch (IOException exception) - { - MessageBox.showException(exception, false); - } - catch (ImageException exception) - { - MessageBox.showException(exception, false); - } - } - - /** - * Called just before an application exits. When this is called, all threads are already killed. - */ - public void onExit() - { - Settings.appSettings = multiEdit.getText(); // Saves the last query. - int n = cbsql.size(); - - if (n > 0) // Saves the queries in the combo box. - { - String[] strings = new String[n]; - int i = -1; - while (++i < n) - strings[i] = (String)cbsql.getItemAt(i); - ByteArrayStream bas = new ByteArrayStream(2000); - DataStream ds = new DataStream(bas); - try - { - ds.writeStringArray(strings); - } catch (IOException exception) - { - MessageBox.showException(exception, false); - } - Settings.appSettingsBin = bas.toByteArray(); - } - else - Settings.appSettingsBin = null; - } - - /** - * Adds the grid to the results tab. - * - * @param caps The grid title. - */ - private void addGrid(String[] caps) - { - Container c = tabCont.getContainer(1); - if (grid != null) - { - c.remove(grid); - grid = null; - } - c.add(grid = new Grid(caps,false)); - c.setBackColor(Color.WHITE); - grid.setBackColor(COLOR_1); - grid.captionsBackColor = COLOR_1; - grid.secondStripeColor = COLOR_2; - grid.setRect(LEFT, TOP, FILL, FILL); - } - - /** - * Updates the status. - * - * @param string The new status string. - */ - private void status(String string) - { - lStatus.setForeColor(Color.BLACK); - lStatus.setText(string); - lStatus.repaintNow(); - } - - /** - * process a sql command. - * - * @param sql The sql command. - */ - private void processSQL(String sql) - { - String command = sql.substring(0, sql.indexOf(' ')).toLowerCase(); - - if (command.equals("select")) // Select. - { - tabCont.setActiveTab(1); - - ResultSet rs = null; - try - { - rs = conn.executeQuery(sql); - } - catch (TableNotClosedException e) - { - // attempt to get the name of the tables used on the query to use the recover table - String sqlLower = sql.toLowerCase(); - int idxStartFrom = sqlLower.indexOf(" from ") + 6; - int idxEndFrom = sqlLower.indexOf(" where "); - if (idxEndFrom == -1) - idxEndFrom = sqlLower.indexOf(" group by "); - if (idxEndFrom == -1) - idxEndFrom = sqlLower.indexOf(" order by "); - String fromClause = idxEndFrom != -1 ? sql.substring(idxStartFrom, idxEndFrom) : sql.substring(idxStartFrom); - String[] tableNames = Convert.tokenizeString(fromClause, ','); - for (int i = tableNames.length - 1 ; i >= 0 ; i--) - conn.recoverTable(tableNames[i].trim()); - - rs = conn.executeQuery(sql); - } - time = Vm.getTimeStamp() - time; - int rows = rs.getRowCount(); - - if (rows <= 0) // Empty answer. - { - status("No records found."); - if (grid != null) - grid.removeAllElements(); - } - else - { - status(rows + " records found."); - - // Uses the result set meta data to get the field labels and display settings. - ResultSetMetaData rsmd = rs.getResultSetMetaData(); - int n = rsmd.getColumnCount(), - cw = fm.charWidth('0'), - i = 0; - String[] titles = new String[n]; - int[] w = new int[n], - a = new int[n]; - - while (++i <= n) - { - if (rsmd.getColumnType(i) == ResultSetMetaData.DOUBLE_TYPE) - { - a[i - 1] = RIGHT; - rs.setDecimalPlaces(i, 2); - } - else a[i - 1] = LEFT; - w[i - 1] = Math.min(80,rsmd.getColumnDisplaySize(i)) * cw / 2; - titles[i - 1] = rsmd.getColumnLabel(i); - } - Container container = tabCont.getContainer(1); - if (grid != null) - { - container.remove(grid); - grid = null; - } - container.add(grid = new Grid(titles, w, a, false)); - grid.setBackColor(COLOR_1); - grid.captionsBackColor = COLOR_1; - grid.secondStripeColor = COLOR_2; - grid.setRect(LEFT, TOP, FILL, FILL); - - // Shows the query results. - grid.setItems(getStrings(rs)); - } - } - - // Commands that update the database. - else if (command.equals("insert") || command.equals("update") || command.equals("delete") || command.equals("drop") || command.equals("alter")) - { - int res = conn.executeUpdate(sql); - time = Vm.getTimeStamp() - time; - - if (res >= 0) - status(res + " records affected."); - } - else if (command.equals("purge")) - { - int res = conn.purge(sql.substring(6).trim()); - if (res >= 0) - status(res + " records affected."); - } - else // Creates a table or an index. - { - conn.execute(sql); - time = Vm.getTimeStamp() - time; - } - } - - /** - * Executes the queries in the multi edit. - */ - private void execute() - { - String[] sqls = Convert.tokenizeString(multiEdit.getText().trim(), '|'); - - grid.removeAllElements(); - - try - { - time = Vm.getTimeStamp(); - int i = -1, - n = sqls.length; - while (++i < n) - { - status("Executing SQL #" + (i + 1)); - processSQL(sqls[i]); - } - status(lStatus.getText()+ " (" + time + "ms)"); - } - catch (RuntimeException exception) - { - MessageBox.showException(exception, true); - } - } - - /** - * Called to process posted events. - * - * @param event The posted event. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED) - { - if (event.target == btCopyResults) - { - StringBuffer sb = new StringBuffer(2048); - Vector vItems = grid.getItemsVector(); - int rows = vItems.size(); - int columns = grid.captions.length; - for (int i = 0 ; i < columns ; i++) - sb.append(grid.captions[i]).append('\t'); - for (int i = 0 ; i < rows ; i++) - { - sb.append('\n'); - String[] row = (String[]) vItems.items[i]; - for (int j = 0 ; j < columns ; j++) - sb.append(row[j]).append('\t'); - } - Vm.clipboardCopy(sb.toString()); - } - else if (event.target == menuBar) // Selects an item in the menu bar. - { - switch (menuBar.getSelectedIndex()) - { - case 1: // Changes the application id. - { - InputBox id = new InputBox("Database ID", "Please enter the new id", databaseId, new String[]{"Ok"}); - id.popup(); - String answer = id.getValue(); - if (!answer.equals(databaseId)) - { - if (answer.length() != 4) // juliana@227_5: The new application id of SQLConsole must be 4 characters long. - new MessageBox("Error", "The application id must be 4 characters long.").popup(); - else - { - // guich@251_2: SQLConsole can now be used with ascii tables. - databaseId = answer; - connChanged(); - lStatus.setText("App id changed to " + answer); - } - } - break; - } - case 2: // Changes the application id to the default one. - { - databaseId = Settings.applicationId; - connChanged(); - lStatus.setText("App id changed to " + Settings.applicationId); - break; - } - case 4: // set/unset isascii - case 5: // set/unset isascii - connChanged(); // guich@251_2: SQLConsole can now be used with ascii tables. - break; - case 7: // Exits the application. - exit(0); - break; - } - } - if (event.target == cbsql && cbsql.getSelectedIndex() >= 0) // Gets the selected sql command from the combo box. - { - multiEdit.setText(cbsql.getSelectedItem().toString()); - repaint(); - multiEdit.requestFocus(); - } - else if (event.target == btnExe && multiEdit.getText().trim().length() > 0) // Executes the queries. - execute(); - else if (event.target == btAdd) // Adds the queries from the multi edit to the combo box. - { - cbsql.add(multiEdit.getText()); - cbsql.selectLast(); - } - else if (event.target == btRem && cbsql.getSelectedIndex() >= 0) // Removes a block of queries from the combo box. - cbsql.remove(cbsql.getSelectedIndex()); - else if (event.target == btnClr) // Clears the multi edit. - { - multiEdit.setText(""); - Control.repaint(); - grid.removeAllElements(); - multiEdit.requestFocus(); - } - else if (event.target == tabCont) // Changes the tab or sets the focus to it. - { - if (tabCont.getActiveTab() == 0) - multiEdit.requestFocus(); - else - grid.requestFocus(); - } - - // Gets the event from the command, operators o letter buttons. - else if (event.target instanceof PushButtonGroup && getFocus() instanceof MultiEdit) - { - PushButtonGroup pbgroup = (PushButtonGroup)event.target; - String string = pbgroup.getSelectedItem(); - if (string != null) - { - Control c = getFocus(); - keyEvent.target = c; - if (string.equals("�")) - { - keyEvent.key = SpecialKeys.BACKSPACE; - c.onEvent(keyEvent); - } - else - { - if (pbgroup != pbgletters && !(pbgroup == pbgoper && pbgroup.getSelectedIndex() >= OPERS.length - 12) - && !pbgroup.getSelectedItem().endsWith("_") && !pbgroup.getSelectedItem().endsWith(">") && !pbgroup.getSelectedItem().endsWith("<")) - string += " "; - - int n = string.length(), - i = -1; - while (++i < n) - { - keyEvent.key = string.charAt(i); - c.onEvent(keyEvent); - } - } - } - } - } - } - - // guich@251_2: SQLConsole can now be used with ascii tables. - private void connChanged() - { - Settings.appSecretKey = databaseId + "|" + (miAscii.isChecked?"1":"0") + (miCrypto.isChecked?"1":"0"); - if (conn != null) conn.closeAll(); - String pars = null; - if (miAscii.isChecked) - pars = "chars_type=chars_ascii"; - if (miCrypto.isChecked) - pars = pars == null ? "crypto" : pars+";crypto"; - conn = LitebaseConnection.getInstance(databaseId, pars); - } - - /** - * Replacement for ResultSet.getStrings used to avoid using null values on the grid. - * - * @param rs - * @return - */ - private String[][] getStrings(ResultSet rs) - { - int rowCount = rs.getRowCount(); - int colCount = rs.getResultSetMetaData().getColumnCount(); - - String[][] result = new String[rowCount][colCount]; - for (int i = 0 ; rs.next() ; i++) - for (int j = 0 ; j < colCount ; j++) - result[i][j] = rs.isNull(j+1) ? "�" : rs.getString(j + 1); - return result; - } -} diff --git a/LitebaseSDK/src/java/samples/apps/sqlconsole/clear.gif b/LitebaseSDK/src/java/samples/apps/sqlconsole/clear.gif deleted file mode 100644 index 3bbffaadc3..0000000000 Binary files a/LitebaseSDK/src/java/samples/apps/sqlconsole/clear.gif and /dev/null differ diff --git a/LitebaseSDK/src/java/samples/apps/sqlconsole/go.gif b/LitebaseSDK/src/java/samples/apps/sqlconsole/go.gif deleted file mode 100644 index 4faabeb422..0000000000 Binary files a/LitebaseSDK/src/java/samples/apps/sqlconsole/go.gif and /dev/null differ diff --git a/LitebaseSDK/src/java/samples/sys/bench/BenchLitebase.java b/LitebaseSDK/src/java/samples/sys/bench/BenchLitebase.java deleted file mode 100644 index 368c50c383..0000000000 --- a/LitebaseSDK/src/java/samples/sys/bench/BenchLitebase.java +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.bench; -import litebase.*; -import totalcross.sys.*; -import totalcross.ui.*; - -/** - * Performs a benchmark in the LitebaseConnection. - */ -public class BenchLitebase extends MainWindow -{ - /** - * The connection with Litebase. - */ - private LitebaseConnection driver = LitebaseConnection.getInstance("Test"); - - /** - * The progress bar for the inserts. - */ - private ProgressBar pbInserts; - - /** - * The list box for showing the results. - */ - private ListBox results; - - /** - * The number of records to be inserted. - */ - private final static int NRECS = 50000; - - /** - * The number of records dived by two. - */ - private final static int NRECS_DIV2 = NRECS >> 1; - - /** - * The refresh rate of the inserts progress bar. - */ - private final static int REFRESH_MOD = NRECS / 50; - - static - { - Settings.useNewFont = true; - } - - /** - * The constructor. - */ - public BenchLitebase() - { - if (Settings.onJavaSE) - totalcross.sys.Settings.showDesktopMessages = false; - Vm.tweak(Vm.TWEAK_DUMP_MEM_STATS,true); - Vm.debug(Vm.ALTERNATIVE_DEBUG); - } - - /** - * Creates the table. - */ - private void createTable() - { - try - { - driver.executeUpdate("drop table person"); - } - catch (DriverException exception) {} - - log("Creating tables..."); - driver.execute("create table PERSON (NAME CHAR(8))"); - driver.setRowInc("person", NRECS); // Allocates NRECS at once. - } - - /** - * inserts the records using prepared statement. - * - * @return The time taken for the operation. - */ - private int insertWithPS() - { - StringBuffer sb = new StringBuffer("a"); // Savea some gc() time. - int refresh = REFRESH_MOD, - time = Vm.getTimeStamp(), - i = 0; - PreparedStatement ps = driver.prepareStatement("insert into person values (?)"); - - while (++i <= NRECS_DIV2) - { - ps.setString(0, sb.append(i).toString()); - ps.executeUpdate(); - if (refresh-- == 0) - { - pbInserts.setValue(500 * i / NRECS); - refresh = REFRESH_MOD; - } - sb.setLength(1); - } - - log("Creation time (PrepStat): " + (time = Vm.getTimeStamp() - time) + "ms"); - return time; - } - - /** - * inserts the records using normal statements. - * - * @return The time taken for the operation. - */ - private int insertNormal() - { - StringBuffer sb = new StringBuffer("a"); // Saves some gc() time. - int refresh = REFRESH_MOD, - i = NRECS_DIV2, - time = Vm.getTimeStamp(); - - sb.setLength(0); - sb.append("insert into person values ('a"); - while (++i <= NRECS) - { - sb.setLength(29); - driver.executeUpdate(sb.append(i).append("')").toString()); - if (refresh-- == 0) - { - pbInserts.setValue(500 * i / NRECS); - refresh = REFRESH_MOD; - } - } - driver.setRowInc("person", -1); // Returns the inc to the default value. - pbInserts.setValue(500); - log("Creation time (normal): " + (time = Vm.getTimeStamp() - time) + "ms"); - return time; - } - - /** - * Selects the before last element. - * - * @return The time taken for the operation. - */ - private int selectBeforeLast() - { - Vm.gc(); - log("Select name = 'a" + (NRECS - 1) + "'"); - - int time = Vm.getTimeStamp(); - ResultSet resultSet = driver.executeQuery("select * from person where name = 'a" + (NRECS - 1) + "'"); - - time = Vm.getTimeStamp() - time; - log("-> Found " + resultSet.getRowCount() + " elements"); - if (resultSet.next()) - log("-> Found: " + resultSet.getString(1)); - else - log("*** Not found..."); - log("Finished: " + time + "ms"); - return time; - } - - /** - * Selects all the elements beginning with a9. - * - * @return The time taken for the operation. - */ - private int selectLikeA9() - { - Vm.gc(); - log("Select like 'a9%'"); - StringBuffer sb = new StringBuffer(); - int time = Vm.getTimeStamp(), - i = -1; - ResultSet resultSet = driver.executeQuery("select * from person where name like 'a9%'"); - - time = Vm.getTimeStamp() - time; - log("-> Found " + resultSet.getRowCount() + " elements"); - - while (++ i < 5 && resultSet.next()) - sb.append(' ').append(resultSet.getString(1)); - log("First 5:" + sb); - log("Finished: " + time + "ms"); - return time; - } - - /** - * Creates the index. - * - * @return The time taken for the operation. - */ - private int createIndex() - { - int time = Vm.getTimeStamp(); - - driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - time = Vm.getTimeStamp() - time; - log("= Index creation time: " + time + "ms"); - return time; - } - - /** - * Selects all the rows of the table. - * - * @return The time taken for the operation. - */ - private int selectStar() - { - Vm.gc(); - log("Select star"); - int time = Vm.getTimeStamp(); - ResultSet resultSet = driver.executeQuery("select * from person"); - - time = Vm.getTimeStamp() - time; - log("-> Found " + resultSet.getRowCount() + " elements"); - if (resultSet.next()) - log("-> Found: " + resultSet.getString(1)); - else - log("*** Not found..."); - log("Finished: " + time + "ms"); - return time; - } - - /** - * Fetches the number of rows of the table. - * - * @return The time taken for the operation. - */ - private int selectCountStar() - { - Vm.gc(); - log("Select count(*)"); - int tempo = Vm.getTimeStamp(); - ResultSet resultSet = driver.executeQuery("select count(*) as number from person"); - - tempo = Vm.getTimeStamp() - tempo; - log("-> Found " + resultSet.getRowCount() + " elements"); - if (resultSet.next()) - log("-> Found: " + resultSet.getString(1)); - else - log("*** Not found..."); - log("Finished: " + tempo + "ms"); - return tempo; - } - - /** - * Selects the maximum element. - * - * @return The time taken for the operation. - */ - private int selectMax() - { - Vm.gc(); - log("Select max()"); - int time = Vm.getTimeStamp(); - ResultSet resultSet = driver.executeQuery("select max(name) as mname from person where name >= 'a0'"); - - time = Vm.getTimeStamp() - time; - log("-> Found " + resultSet.getRowCount() + " elements"); - if (resultSet.next()) - log("-> Found: " + resultSet.getString(1)); - else - log("*** Not found..."); - log("Finished: " + time + "ms"); - return time; - } - - /** - * Does a select with order by. - * - * @return The time taken for the operation. - */ - private int selectOrderBy() - { - Vm.gc(); - log("Select with order by"); - int time = Vm.getTimeStamp(); - ResultSet resultSet = driver.executeQuery("select * from person order by name"); - - time = Vm.getTimeStamp() - time; - log("-> Found " + resultSet.getRowCount() + " elements"); - if (resultSet.next()) - log("-> Found: " + resultSet.getString(1)); - else - log("*** Not found..."); - log("Finished: " + time + "ms"); - return time; - } - - /** - * Logs the results on the debug console and on the list box. - * - * @param string The string to be logged. - */ - private void log(String string) - { - Vm.debug(string); - results.add(string); - } - - /** - * Initializes the user interface. - */ - public void initUI() - { - // User interface. - ProgressBar pbTotal = new ProgressBar(0, 14); - add(pbInserts = new ProgressBar(0, 500), CENTER, AFTER + 5); - add(pbTotal, CENTER, AFTER + 5); - add(results = new ListBox()); - pbInserts.suffix = "00 of " + NRECS; - pbTotal.suffix = " of 14"; - results.setRect(LEFT, AFTER + 5, FILL, FILL); - - // Executes the bench operations. - repaintNow(); - createTable(); - pbTotal.setValue(1); - int time1 = insertWithPS(); - pbTotal.setValue(2); - int time2 = insertNormal(); - pbTotal.setValue(3); - int time3 = selectBeforeLast(); - pbTotal.setValue(4); - int time4 = selectLikeA9(); - pbTotal.setValue(5); - int time5 = selectMax(); - pbTotal.setValue(6); - int time6 = createIndex(); - pbTotal.setValue(7); - int time7 = selectBeforeLast(); - pbTotal.setValue(8); - int time8 = selectLikeA9(); - pbTotal.setValue(9); - int time9 = selectMax(); - pbTotal.setValue(10); - int time10 = selectStar(); - pbTotal.setValue(11); - int time11 = selectCountStar(); - pbTotal.setValue(12); - int time12 = selectOrderBy(); - pbTotal.setValue(13); - - driver.executeUpdate("drop index * on person"); - driver.executeUpdate("alter table person add primary key(name)"); - - int time13 = selectOrderBy(); - pbTotal.setValue(14); - - // Logs the results. - log(time1+ " " + time2); - log(time3 + " " + time4 + " " + time5); - log(time6 + " "); - log(time7 + " " + time8 + " " + time9); - log(time10 + " " + time11 + " " + time12 + " " + time13); - log("total: " + (time1 + time2 + time3 + time4 + time5 + time6 + time7 + time8 + time9 + time10 + time11 + time12)); - log("Results are also in the console"); - - results.selectLast(); - results.requestFocus(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/bench/BenchLitebase_appIcon.gif b/LitebaseSDK/src/java/samples/sys/bench/BenchLitebase_appIcon.gif deleted file mode 100644 index 5fd964458b..0000000000 Binary files a/LitebaseSDK/src/java/samples/sys/bench/BenchLitebase_appIcon.gif and /dev/null differ diff --git a/LitebaseSDK/src/java/samples/sys/testcases/AllTests.java b/LitebaseSDK/src/java/samples/sys/testcases/AllTests.java deleted file mode 100644 index 9d690793dc..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/AllTests.java +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -// Created on 29/04/2004 -import litebase.*; -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.unit.*; -import totalcross.ui.*; -import totalcross.ui.event.*; - -/** - * The class that executes all test cases. - */ -public class AllTests extends TestSuite -{ - /** - * Indicates if the logger is to be used or not during AllTests. - */ - static boolean useLogger; - - /** - * Indicates if the connections of the tests except for TestAsciiTables and TestSourcePath use ascii connections. - */ - private static boolean isAscii; - - /** - * Indicates if the connections of the tests except for TestAsciiTables and TestSourcePath use cryptography. - */ - static boolean useCrypto; - - static - { - Settings.useNewFont = true; - } - - /** - * Constructs all the test cases. Needs to be used with TotalCross. - */ - public AllTests() - { - super("Litebase Test Suite"); - if (Settings.platform.equals(Settings.ANDROID)) - Vm.debug(Vm.ALTERNATIVE_DEBUG); - addTestCase(TestAddColumn.class); - addTestCase(TestAsciiTables.class); // juliana@210_2: now Litebase supports tables with ascii strings. - addTestCase(TestBigJoins.class); - addTestCase(TestBlob.class); - addTestCase(TestCachedRows.class); - addTestCase(TestClosedLitebaseAndProcessLogs.class); - addTestCase(TestComposedIndexAndPK.class); - addTestCase(TestCryptoTables.class); - addTestCase(TestDate_DateTime.class); - addTestCase(TestDeleteAndMetaData.class); - addTestCase(TestDeleteAndPurge.class); - addTestCase(TestDrop.class); - addTestCase(TestDuplicateEntry.class); - addTestCase(TestEndianess.class); - addTestCase(TestIndexIneqAndLike.class); - addTestCase(TestIndexRebalance.class); - addTestCase(TestInvalidArguments.class); - addTestCase(TestJoin.class); - addTestCase(TestLogger.class); - addTestCase(TestMaxMin.class); - addTestCase(TestMultipleConnection.class); - addTestCase(TestNullAndDefaultValues.class); - addTestCase(TestOrderBy.class); - addTestCase(TestOrderByIndices.class); - addTestCase(TestPreparedStatement.class); - addTestCase(TestPrimaryKey.class); - addTestCase(TestRename.class); - addTestCase(TestResultSet.class); // juliana@211_4: solved bugs with result set dealing. - addTestCase(TestReIndex2rowId.class); - addTestCase(TestRowIdAndPurge.class); - addTestCase(TestRowIterator.class); - addTestCase(TestSelectClause_AggFunctions.class); - addTestCase(TestSourcePath.class); - addTestCase(TestSQLFunctions.class); - addTestCase(TestTableRecovering.class); - addTestCase(TestThread.class); - addTestCase(TestVirtualRecords.class); - addTestCase(TestWhereClause_Basic.class); - addTestCase(TestWhereClause_Caseless.class); - addTestCase(TestWhereClause_Indexes.class); - Vm.tweak(Vm.TWEAK_DUMP_MEM_STATS, true); - } - - /** - * Initializes the user interface. - */ - public void initUI() // rnovais@570_77 - { - String appSecretKey = Settings.appSecretKey; - if (appSecretKey != null && appSecretKey.length() < 4) - appSecretKey = Settings.appSecretKey = null; - useLogger = appSecretKey != null && appSecretKey.charAt(0) == 'y'; - isAscii = appSecretKey != null && appSecretKey.charAt(1) == 'y'; - useCrypto = appSecretKey != null && appSecretKey.charAt(2) == 'y'; - LitebaseConnection.logOnlyChanges = appSecretKey != null && appSecretKey.charAt(3) == 'y'; - if (useLogger) - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - super.initUI(); - setMenuBar(mbar = new MenuBar(new MenuItem[][]{mbar.getMenuItems()[0], - {new MenuItem("Config"), new MenuItem("isAscii", isAscii), new MenuItem("useCrypto", useCrypto), - new MenuItem("Drop all tables")}, - {new MenuItem("Logs"), new MenuItem("Use Logger", useLogger), - new MenuItem("Log only changes", LitebaseConnection.logOnlyChanges), new MenuItem("Erase All Loggers")}})); - } - - /** - * Detects the menu items events other than File. - * - * @param event The event being handled. - */ - public void onEvent(Event event) - { - if (event.type == ControlEvent.PRESSED && event.target == mbar) - { - switch (mbar.getSelectedIndex()) - { - case 101: - isAscii = !isAscii; - break; - case 102: - useCrypto = !useCrypto; - break; - case 103: // Drops All Tables. - dropAllTables(); - break; - case 201: // Sets or unsets the logger usage. - useLogger = !useLogger; - if (useLogger) - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - else - { - if (LitebaseConnection.logger != null) - { - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - } - } - break; - case 202: - LitebaseConnection.logOnlyChanges = !LitebaseConnection.logOnlyChanges; - break; - case 203: // Deletes all the logger files. - LitebaseConnection.deleteLogFiles(); - } - Settings.appSecretKey = (useLogger? "y" : "n") + (isAscii? 'y' : 'n') + (useCrypto? 'y' : 'n') + (LitebaseConnection.logOnlyChanges? 'y' : 'n'); - } - - super.onEvent(event); - } - - /** - * Drops all tables used for the testcases. Notice that all the tables in the folders used by the tests and with the creation id "Test" - * will be erased even if they are from another application. - */ - private void dropAllTables() - { - int i = 7; - String temporario; - String tempPath = Convert.appendPath(Settings.appPath, "temp/"); - - try - { - temporario = Convert.appendPath(File.getCardVolume().getPath(), "tempor�rio/"); - } - catch (IOException exception) - { - temporario = Convert.appendPath(Settings.appPath, "tempor�rio/"); - } - catch (NullPointerException exception) - { - temporario = Convert.appendPath(Settings.appPath, "tempor�rio/"); - } - - // The paths used by AllTests. - String[] paths = {Settings.appPath, Settings.dataPath, tempPath, tempPath + "a/", tempPath + "b/", temporario, "/"}; - - // The subfolders used by AllTests. - File[] folders = new File[7]; - - while (--i >= 0) // Erases the table files from this application. - { - try - { - - if (paths[i] != null) - { - folders[i] = new File(paths[i], File.DONT_OPEN); - LitebaseConnection.dropDatabase("Test", paths[i], -1); - } - } - catch (DriverException exception) {} - catch (IOException exception) {} - } - - i = 6; - while (--i >= 2) // Erases the folders, being careful to erase the empty folders first. - try - { - folders[i].delete(); - } - catch (IOException exception) {} - } - - /** - * Does the tests using the kind of string selected. - * - * @return A driver connection using ascii or unicode characters depending on which option was made and the application id as the application id - * used by Litebase. - */ - static LitebaseConnection getInstance() - { - return LitebaseConnection.getInstance(Settings.applicationId, - "chars_type =" + (isAscii? "ascii" : "unicode") + (useCrypto? "; crypto" : "") + "; path = " + Settings.appPath); - } - - /** - * Does the tests using the kind of string selected. - * - * @param appCrid The application id given by the user. - * @return A driver connection using ascii or unicode characters depending on which option was made. - */ - static LitebaseConnection getInstance(String appCrid) - { - return LitebaseConnection.getInstance(appCrid, - "chars_type =" + (isAscii? "ascii" : "unicode") + (useCrypto? "; crypto" : "") + "; path = " + Settings.appPath); - } - - /** - * Does the tests using the kind of string selected. - * - * @param appCrid The application id given by the user. - * @param path The path where the tables are to be created. - * @return A driver connection using ascii or unicode characters depending on which option was made and the path given by the user. - */ - static LitebaseConnection getInstance(String appCrid, String path) - { - return LitebaseConnection.getInstance(appCrid, - "chars_type =" + (isAscii? "ascii" : "unicode") + (useCrypto? "; crypto" : "") + "; path = " + path); - } -} - diff --git a/LitebaseSDK/src/java/samples/sys/testcases/AllTests_appIcon.gif b/LitebaseSDK/src/java/samples/sys/testcases/AllTests_appIcon.gif deleted file mode 100644 index 3d8d691aed..0000000000 Binary files a/LitebaseSDK/src/java/samples/sys/testcases/AllTests_appIcon.gif and /dev/null differ diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestAddColumn.java b/LitebaseSDK/src/java/samples/sys/testcases/TestAddColumn.java deleted file mode 100644 index 6b004b3937..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestAddColumn.java +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.unit.TestCase; - -/** - * Does tests with add column. - */ -public class TestAddColumn extends TestCase -{ - /** - * The connection with Litebase. - */ - LitebaseConnection driver = AllTests.getInstance("Test"); - - /** - * A time object. - */ - Time time = new Time(); - - /** - * Main test method. - */ - public void testRun() - { - wrongSyntax(); // Tests wrong syntax using add column. - addAfterInsert(); // Tests add column after some inserts. - addAfterCreate(); // Tests add column just after creating the table. - driver.closeAll(); - } - - /** - * Drops and creates the test table. - */ - private void create() - { - try - { - driver.executeUpdate("drop table person"); - } - catch (DriverException exception) {} - - driver.execute("create table person (name char(10))"); - } - - /** - * Tests wrong syntax. - */ - private void wrongSyntax() - { - create(); - try // No type. - { - driver.executeUpdate("alter table person add column"); - fail("1"); - } - catch (SQLParseException exception) {} - try // Invalid type. - { - driver.executeUpdate("alter table person add column x"); - fail("2"); - } - catch (SQLParseException exception) {} - try // No type. - { - driver.executeUpdate("alter table person add x"); - fail("3"); - } - catch (SQLParseException exception) {} - try // An added column can't be declared as primary key. - { - driver.executeUpdate("alter table person add x char(5) primary key"); - fail("4"); - } - catch (SQLParseException exception) {} - try // An added column can't be declared as primary key. - { - driver.executeUpdate("alter table person add x char(5) default 'x' primary key"); - fail("5"); - } - catch (SQLParseException exception) {} - try // An added column declared as not null must have a not null default value. - { - driver.executeUpdate("alter table person add x char(5) not null"); - fail("6"); - } - catch (SQLParseException exception) {} - try // An added column declared as not null must have a not null default value. - { - driver.executeUpdate("alter table person add x char(5) not null default null"); - fail("7"); - } - catch (SQLParseException exception) {} - try // Totally wrong SQL. - { - driver.executeUpdate("alter table person add primary key (name) x char(5)"); - fail("8"); - } - catch (SQLParseException exception) {} - try // Repeated column name. - { - driver.executeUpdate("alter table person add name char(5)"); - fail("9"); - } - catch (SQLParseException exception) {} - try // Repeated column name. - { - driver.executeUpdate("alter table person add rowid int"); - fail("10"); - } - catch (SQLParseException exception) {} - } - - /** - * Adds new columns after doing inserts. - */ - private void addAfterInsert() - { - create(); - int i = 100; - PreparedStatement prepStmt = driver.prepareStatement("insert into person values (?)"); - - time.year = 2012; - time.month = 3; - time.day = 20; - time.hour = 16; - time.minute = 53; - time.second = 42; - time.millis = 0; - - while (--i >= 0) - { - prepStmt.setString(0, "name" + i); - prepStmt.executeUpdate(); - } - - driver.executeUpdate("alter table person add a char(10) default null"); - driver.executeUpdate("alter table person add b short default 5 not null"); - driver.executeUpdate("alter table person add c int"); - driver.executeUpdate("alter table person add d long"); - driver.executeUpdate("alter table person add e double"); - driver.executeUpdate("alter table person add f datetime default '2012/03/20 16:53:42'"); - driver.executeUpdate("alter table person add g blob(1 K)"); - - ResultSet resultSet = driver.executeQuery("select * from person"); - assertEquals(100, resultSet.getRowCount()); - - i = 100; - while (resultSet.next()) - { - assertEquals("name" + (--i), resultSet.getString(1)); - assertNull(resultSet.getString("a")); - assertTrue(resultSet.isNull("a")); - assertEquals(5, resultSet.getShort(3)); - assertFalse(resultSet.isNull(3)); - assertNull(resultSet.getString("c")); - assertTrue(resultSet.isNull("c")); - assertNull(resultSet.getString(5)); - assertTrue(resultSet.isNull(5)); - assertNull(resultSet.getString("e")); - assertTrue(resultSet.isNull("e")); - assertEquals(time, resultSet.getDateTime(7)); - assertFalse(resultSet.isNull(7)); - assertNull(resultSet.getString("g")); - assertTrue(resultSet.isNull("g")); - } - resultSet.close(); - - time.day = 26; - time.month = 6; - time.year = 1979; - time.millis = time.second = time.minute = time.hour = 0; - - driver.executeUpdate("update person set a = 'a', b = 1, c = null, d = 2, e = 3.5, f = '1979/06/26'"); - assertEquals(i = 100, (resultSet = driver.executeQuery("select * from person")).getRowCount()); - while (resultSet.next()) - { - assertEquals("name" + (--i), resultSet.getString(1)); - assertEquals("a", resultSet.getString("a")); - assertFalse(resultSet.isNull("a")); - assertEquals(1, resultSet.getShort(3)); - assertFalse(resultSet.isNull(3)); - assertNull(resultSet.getString("c")); - assertTrue(resultSet.isNull("c")); - assertEquals(2, resultSet.getLong(5)); - assertFalse(resultSet.isNull(5)); - assertEquals(3.5, resultSet.getDouble("e"), 0.001); - assertFalse(resultSet.isNull("e")); - assertEquals(time, resultSet.getDateTime(7)); - assertFalse(resultSet.isNull(7)); - assertNull(resultSet.getString("g")); - assertTrue(resultSet.isNull("g")); - - } - resultSet.close(); - - driver.executeUpdate("delete from person"); - - coreTest(); - } - - /** - * Adds new columns after creating the table. - */ - private void addAfterCreate() - { - create(); - driver.executeUpdate("alter table person add a char(10) default null"); - driver.executeUpdate("alter table person add b short default 5 not null"); - driver.executeUpdate("alter table person add c int"); - driver.executeUpdate("alter table person add d long"); - driver.executeUpdate("alter table person add e double"); - driver.executeUpdate("alter table person add f datetime default '2012/03/20 16:53:42'"); - driver.executeUpdate("alter table person add g blob(1 K)"); - - coreTest(); - } - - /** - * Executes the main test. - */ - private void coreTest() - { - doInserts(); - doSelect(); - driver.purge("person"); - doSelect(); - addAndMetaData(); - addAndRecover(); - addAndRowIterator(); - addAndIndices(); - addTooManyColumns(); - } - - /** - * Tests result set meta data with add column. - */ - private void addAndMetaData() - { - ResultSet resultSet = driver.executeQuery("select * from person"); - ResultSetMetaData metaData = resultSet.getResultSetMetaData(); - - time.year = 2012; - time.month = 3; - time.day = 20; - time.hour = 16; - time.minute = 53; - time.second = 42; - time.millis = 0; - - assertEquals(8, metaData.getColumnCount()); - - assertEquals(10, metaData.getColumnDisplaySize(1)); - assertEquals("name", metaData.getColumnLabel(1)); - assertEquals(ResultSetMetaData.CHAR_TYPE, metaData.getColumnType(1)); - assertEquals("chars", metaData.getColumnTypeName(1)); - assertEquals("person", metaData.getColumnTableName(1)); - assertEquals("person", metaData.getColumnTableName("name")); - assertFalse(metaData.hasDefaultValue(1)); - assertFalse(metaData.hasDefaultValue("name")); - assertFalse(metaData.isNotNull(1)); - assertFalse(metaData.isNotNull("name")); - assertNull(metaData.getDefaultValue(1)); - assertNull(metaData.getDefaultValue("name")); - - assertEquals(10, metaData.getColumnDisplaySize(2)); - assertEquals("a", metaData.getColumnLabel(2)); - assertEquals(ResultSetMetaData.CHAR_TYPE, metaData.getColumnType(2)); - assertEquals("chars", metaData.getColumnTypeName(2)); - assertEquals("person", metaData.getColumnTableName(2)); - assertEquals("person", metaData.getColumnTableName("a")); - assertFalse(metaData.hasDefaultValue(2)); - assertFalse(metaData.hasDefaultValue("a")); - assertFalse(metaData.isNotNull(2)); - assertFalse(metaData.isNotNull("a")); - assertNull(metaData.getDefaultValue(2)); - assertNull(metaData.getDefaultValue("a")); - - assertEquals(6, metaData.getColumnDisplaySize(3)); - assertEquals("b", metaData.getColumnLabel(3)); - assertEquals(ResultSetMetaData.SHORT_TYPE, metaData.getColumnType(3)); - assertEquals("short", metaData.getColumnTypeName(3)); - assertEquals("person", metaData.getColumnTableName(3)); - assertEquals("person", metaData.getColumnTableName("b")); - assertTrue(metaData.hasDefaultValue(3)); - assertTrue(metaData.hasDefaultValue("b")); - assertTrue(metaData.isNotNull(3)); - assertTrue(metaData.isNotNull("b")); - assertEquals("5", metaData.getDefaultValue(3)); - assertEquals("5", metaData.getDefaultValue("b")); - - assertEquals(11, metaData.getColumnDisplaySize(4)); - assertEquals("c", metaData.getColumnLabel(4)); - assertEquals(ResultSetMetaData.INT_TYPE, metaData.getColumnType(4)); - assertEquals("int", metaData.getColumnTypeName(4)); - assertEquals("person", metaData.getColumnTableName(4)); - assertEquals("person", metaData.getColumnTableName("c")); - assertFalse(metaData.hasDefaultValue(4)); - assertFalse(metaData.hasDefaultValue("c")); - assertFalse(metaData.isNotNull(4)); - assertFalse(metaData.isNotNull("c")); - assertNull(metaData.getDefaultValue(4)); - assertNull(metaData.getDefaultValue("c")); - - assertEquals(20, metaData.getColumnDisplaySize(5)); - assertEquals("d", metaData.getColumnLabel(5)); - assertEquals(ResultSetMetaData.LONG_TYPE, metaData.getColumnType(5)); - assertEquals("long", metaData.getColumnTypeName(5)); - assertEquals("person", metaData.getColumnTableName(5)); - assertEquals("person", metaData.getColumnTableName("d")); - assertFalse(metaData.hasDefaultValue(5)); - assertFalse(metaData.hasDefaultValue("d")); - assertFalse(metaData.isNotNull(5)); - assertFalse(metaData.isNotNull("d")); - assertNull(metaData.getDefaultValue(5)); - assertNull(metaData.getDefaultValue("d")); - - assertEquals(21, metaData.getColumnDisplaySize(6)); - assertEquals("e", metaData.getColumnLabel(6)); - assertEquals(ResultSetMetaData.DOUBLE_TYPE, metaData.getColumnType(6)); - assertEquals("double", metaData.getColumnTypeName(6)); - assertEquals("person", metaData.getColumnTableName(6)); - assertEquals("person", metaData.getColumnTableName("e")); - assertFalse(metaData.hasDefaultValue(6)); - assertFalse(metaData.hasDefaultValue("e")); - assertFalse(metaData.isNotNull(6)); - assertFalse(metaData.isNotNull("e")); - assertNull(metaData.getDefaultValue(6)); - assertNull(metaData.getDefaultValue("e")); - - assertEquals(31, metaData.getColumnDisplaySize(7)); - assertEquals("f", metaData.getColumnLabel(7)); - assertEquals(ResultSetMetaData.DATETIME_TYPE, metaData.getColumnType(7)); - assertEquals("datetime", metaData.getColumnTypeName(7)); - assertEquals("person", metaData.getColumnTableName(7)); - assertEquals("person", metaData.getColumnTableName("f")); - assertTrue(metaData.hasDefaultValue(7)); - assertTrue(metaData.hasDefaultValue("f")); - assertFalse(metaData.isNotNull(7)); - assertFalse(metaData.isNotNull("f")); - assertEquals("2012/03/20 16:53:42:000", metaData.getDefaultValue(7)); - assertEquals("2012/03/20 16:53:42:000", metaData.getDefaultValue("f")); - - assertEquals(-1, metaData.getColumnDisplaySize(8)); - assertEquals("g", metaData.getColumnLabel(8)); - assertEquals(ResultSetMetaData.BLOB_TYPE, metaData.getColumnType(8)); - assertEquals("blob", metaData.getColumnTypeName(8)); - assertEquals("person", metaData.getColumnTableName(8)); - assertEquals("person", metaData.getColumnTableName("g")); - assertFalse(metaData.hasDefaultValue(8)); - assertFalse(metaData.hasDefaultValue("g")); - assertFalse(metaData.isNotNull(8)); - assertFalse(metaData.isNotNull("g")); - assertNull(metaData.getDefaultValue(8)); - assertNull(metaData.getDefaultValue("g")); - - resultSet.close(); - } - - /** - * Does the inserts for the tests. - */ - private void doInserts() - { - int i = 100; - PreparedStatement prepStmt = driver.prepareStatement("insert into person values (?, ?, ?, ?, ?, ?, ?, ?)"); - - time.year = 2012; - time.month = 3; - time.day = 20; - time.hour = 16; - time.minute = 53; - time.second = 42; - time.millis = 0; - - while (--i >= 0) - { - prepStmt.setString(0, "name" + i); - prepStmt.setString(1, "a" + i); - prepStmt.setShort(2, (short)i); - prepStmt.setInt(3, i); - prepStmt.setLong(4, i); - prepStmt.setDouble(5, i); - prepStmt.setDateTime(6, time); - prepStmt.setBlob(7, ("name" + i).getBytes()); - prepStmt.executeUpdate(); - } - } - - /** - * Does a select for the tests. - */ - private void doSelect() - { - ResultSet resultSet = driver.executeQuery("select * from person"); - int i = 100; - assertEquals(100, resultSet.getRowCount()); - while (resultSet.next()) - { - assertEquals("name" + (--i), resultSet.getString("name")); - assertEquals("a" + i, resultSet.getString(2)); - assertEquals(i, resultSet.getShort("b")); - assertEquals(i, resultSet.getInt(4)); - assertEquals(i, resultSet.getLong("d")); - assertEquals(i, resultSet.getDouble(6), 0.001); - assertEquals(time, resultSet.getDateTime("f")); - assertEquals(("name" + i).getBytes(), resultSet.getBlob(8)); - } - resultSet.close(); - } - - /** - * Tests table recovering after adding a column. - */ - private void addAndRecover() - { - try - { - String path = driver.getSourcePath() + "Test-person.db"; - - driver.closeAll(); - - File dbFile = new File(path, File.READ_WRITE); // The table is closed after recovering it. - byte[] oneByte = new byte[1]; - - // Pretends that the table was not closed correctly. - dbFile.setPos(6); - dbFile.readBytes(oneByte, 0, 1); - - if (AllTests.useCrypto) - oneByte[0] ^= 0xAA; - oneByte[0] = (byte)(oneByte[0] & 2); - if (AllTests.useCrypto) - oneByte[0] ^= 0xAA; - dbFile.setPos(6); - dbFile.writeBytes(oneByte, 0, 1); - dbFile.close(); - } - catch (IOException exception) - { - fail("11"); - } - driver = AllTests.getInstance("Test"); - - try - { - doSelect(); - fail("12"); - } - catch (TableNotClosedException exception) - { - driver.recoverTable("person"); - doSelect(); - } - } - - /** - * Tests add column with row iterator. - */ - private void addAndRowIterator() - { - RowIterator it = driver.getRowIterator("PERSON"); // Gets the row iterator. - int i = 100; - - while (it.next()) - { - // Confirms the row id and that the NEW attribute is set. - assertTrue(101 - i == it.rowid || 201 - i == it.rowid); - assertEquals(RowIterator.ROW_ATTR_NEW, it.attr); - - // Checks the values of the iterator. - assertEquals("name" + (--i), it.getString(1)); - assertFalse(it.isNull(1)); - assertEquals("a" + i, it.getString(2)); - assertFalse(it.isNull(2)); - assertEquals(i, it.getShort(3)); - assertFalse(it.isNull(3)); - assertEquals(i, it.getInt(4)); - assertFalse(it.isNull(4)); - assertEquals(i, it.getLong(5)); - assertFalse(it.isNull(5)); - assertEquals(i, it.getDouble(6), 0.001); - assertFalse(it.isNull(6)); - assertEquals(time, it.getDateTime(7)); - assertFalse(it.isNull(7)); - assertEquals(("name" + i).getBytes(), it.getBlob(8)); - assertFalse(it.isNull(8)); - } - - it.close(); - } - - /** - * Creates indices and tests added columns. - */ - private void addAndIndices() - { - driver.execute("create index idx on person(name)"); - driver.execute("create index idx on person(a)"); - driver.execute("create index idx on person(b)"); - driver.execute("create index idx on person(c)"); - driver.execute("create index idx on person(d)"); - driver.execute("create index idx on person(e)"); - driver.execute("create index idx on person(f)"); - testSelectWithIndices(); - - driver.executeUpdate("drop index * on person"); - driver.execute("create index idx on person (name, a, b, c, d, e, f)"); - testSelectWithIndices(); - } - - /** - * Tests a select with the created indices above. - */ - private void testSelectWithIndices() - { - ResultSet resultSet; - PreparedStatement psSelect = driver.prepareStatement("select * from person where name = ? and a = ? and b = ? and c = ? and d = ? and e = ? " - + "and f = '2012/03/20 16:53:42'"); - - int i = 100; - while (--i >= 0) - { - psSelect.setString(0, "name" + i); - psSelect.setString(1, "a" + i); - psSelect.setShort(2, (short)i); - psSelect.setInt(3, i); - psSelect.setLong(4, i); - psSelect.setDouble(5, i); - - assertEquals(1, (resultSet = psSelect.executeQuery()).getRowCount()); - resultSet.first(); - - assertEquals("name" + i, resultSet.getString("name")); - assertEquals("a" + i, resultSet.getString(2)); - assertEquals(i, resultSet.getShort("b")); - assertEquals(i, resultSet.getInt(4)); - assertEquals(i, resultSet.getLong("d")); - assertEquals(i, resultSet.getDouble(6), 0.001); - assertEquals(time, resultSet.getDateTime("f")); - assertEquals(("name" + i).getBytes(), resultSet.getBlob(8)); - - assertFalse(resultSet.next()); - resultSet.close(); - } - } - - /** - * Adds many columns till exceeding the maximum number of columns. - */ - private void addTooManyColumns() - { - try - { - int i = 246; - StringBuffer sBuffer = new StringBuffer(50); - - sBuffer.append("alter table person add a"); - while (--i >= 0) - { - sBuffer.setLength(24); - sBuffer.append(i); - sBuffer.append(" int"); - driver.executeUpdate(sBuffer.toString()); - } - - fail("13"); - } - catch (SQLParseException exception) {} - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestAsciiTables.java b/LitebaseSDK/src/java/samples/sys/testcases/TestAsciiTables.java deleted file mode 100644 index 36c8fbe6fc..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestAsciiTables.java +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.unit.TestCase; - -// juliana@210_2: now Litebase supports tables with ascii strings. -/** - * Does tests with ascii and unicode tables. - */ -public class TestAsciiTables extends TestCase -{ - /** - * Main test method. - */ - public void testRun() - { - String tempPath = Convert.appendPath(Settings.appPath, "temp/"); // The path used. - - // Creates a table within an ascii connection. - LitebaseConnection conn = LitebaseConnection.getInstance("Test", "chars_type = ascii; path = " + tempPath); - - // The table does not exist yet. - assertFalse(conn.isOpen("person")); - - if (conn.exists("person")) - conn.executeUpdate("drop table person"); - conn.execute("CREATE table PERSON (NAME CHAR(10))"); - assertTrue(conn.isOpen("person")); - conn.closeAll(); - - // Trying to use this table in an unicode connection must fail. - conn = LitebaseConnection.getInstance("Test", tempPath); - try - { - conn.executeUpdate("insert into person values ('Juliana')"); - fail("1"); - } - catch (DriverException exception) {} - conn.executeUpdate("drop table person"); - conn.closeAll(); - - // Creates a table within an unicode connection. - (conn = LitebaseConnection.getInstance("Test", "chars_type = unicode; path = " + tempPath)).execute("create table person (name char(10))"); - conn.closeAll(); - - // Trying to use this table in an ascii connection must fail. - conn = LitebaseConnection.getInstance("Test", "chars_type = ascii; path = " + tempPath); - try - { - conn.execute("create index idx on person(name)"); - fail("2"); - } - catch (DriverException exception) {} - conn.executeUpdate("drop table person"); - conn.closeAll(); - - // Creates two connections which only differ in the string types. They must be different. - LitebaseConnection connAux = LitebaseConnection.getInstance("Test", "path = " + tempPath + " ; chars_type = ascii"); - assertNotEquals(conn = LitebaseConnection.getInstance("Test", "path = " + tempPath + ";chars_type = unicode"), connAux); - - // Since the paths and the application id are the same, the second connection can't create the table. - conn.execute("create table person (name char(10))"); - - conn.closeAll(); - try - { - connAux.execute("create table person (name char(10))"); - fail("3"); - } - catch (AlreadyCreatedException exception) {} - - try // Trying to use an unicode table in an ascii connection must fail. - { - connAux.executeQuery("select * from person"); - fail("4"); - } - catch (DriverException exception) {} - connAux.closeAll(); - - // Trying to use an unicode table in an ascii connection must fail, but it can't fail in an unicode connection. - conn = LitebaseConnection.getInstance("Test", "chars_type = unicode; path = " + tempPath); - connAux = LitebaseConnection.getInstance("Test", "chars_type = ascii; path = " + tempPath); - try - { - connAux.executeQuery("select * from person"); - fail("5"); - } - catch (DriverException exception) {} - assertNotNull(conn.executeQuery("select * from person")); - conn.executeUpdate("drop table person"); - conn.closeAll(); - connAux.closeAll(); - - // Creates and populates an ascii and an unicode table. - conn = LitebaseConnection.getInstance("Test", "chars_type = unicode; path = " + tempPath); - if (conn.exists("person1")) - conn.executeUpdate("drop table person1"); - conn.execute("create table person1 (name char(10) primary key)"); - connAux = LitebaseConnection.getInstance("Test", "chars_type = ascii; path = " + tempPath); - if (connAux.exists("person2")) - connAux.executeUpdate("drop table person2"); - connAux.execute("create table person2 (name char(10) primary key)"); - - int i = -1; - while (++i < 100) - { - conn.executeUpdate("insert into person1 values ('Name" + i + "')"); - connAux.executeUpdate("insert into person2 values ('Name" + i + "')"); - } - - // It is not possible to use a table within a connection with a different kind of string. - conn.executeUpdate("insert into person1 values ('Name100')"); - conn.closeAll(); - try - { - connAux.executeUpdate("insert into person1 values ('Name100')"); - fail("6"); - } - catch (DriverException exception) {} - connAux.executeUpdate("insert into person2 values ('Name100')"); - connAux.closeAll(); - conn = LitebaseConnection.getInstance("Test", "chars_type = unicode; path = " + tempPath); - connAux = LitebaseConnection.getInstance("Test", "chars_type = ascii; path = " + tempPath); - try - { - - conn.executeUpdate("insert into person2 values ('Name100')"); - fail("7"); - } - catch (DriverException exception) {} - - // Asserts that the elements were saved correctly. - ResultSet rs1 = conn.executeQuery("select rowid, name from person1"), - rs2 = connAux.executeQuery("select rowid, name from person2"); - assertEquals(101, rs1.getRowCount()); - assertEquals(101, rs2.getRowCount()); - i = -1; - while (++i <= 100) - { - rs1.next(); - rs2.next(); - assertEquals("Name" + i, new String(rs1.getChars(2))); - assertEquals("Name" + i, new String(rs2.getChars("name"))); - } - rs1.close(); - rs2.close(); - - // juliana@214_5 juliana@214_6: the strings must be trimmed. - connAux.executeUpdate("drop table person2"); - connAux.execute("create table person2 (name char(1) primary key)"); - connAux.executeUpdate("insert into person2 values('')"); - connAux.executeUpdate("insert into person2 values('\\'')"); - - try // 'A is to be stored as ' since the field size is 1. - { - connAux.executeUpdate("insert into person2 values('\\'A')"); - fail("8"); - } - catch (PrimaryKeyViolationException exception) {} - - // Tests if the data was inserted correctly. - assertTrue((rs2 = connAux.executeQuery("select * from person2")).next()); - assertEquals("", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - assertTrue((rs2 = connAux.executeQuery("select * from person2 where name like '\\''")).next()); - assertEquals("'", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - - connAux.executeUpdate("drop table person2"); - connAux.execute("create table person2 (name char(2) primary key)"); - connAux.executeUpdate("insert into person2 values('')"); - connAux.executeUpdate("insert into person2 values('\\'')"); - connAux.executeUpdate("insert into person2 values('\\'A')"); - - try // 'AA is to be stored as 'A since the field size is 2. - { - connAux.executeUpdate("insert into person2 values('\\'AA')"); - fail("9"); - } - catch (PrimaryKeyViolationException exception) {} - - // Tests if the data was inserted correctly. - assertTrue((rs2 = connAux.executeQuery("select * from person2")).next()); - assertEquals("", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'A", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - assertTrue((rs2 = connAux.executeQuery("select * from person2 where name like '\\'%'")).next()); - assertEquals("'", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'A", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - - // The tables are opened by its current connections. - assertTrue(conn.isOpen("PERSON1")); - assertTrue(connAux.isOpen("PERSON2")); - - // The tables are opened by the other connection. - assertFalse(connAux.isOpen("PERSON1")); - assertFalse(conn.isOpen("PERSON2")); - - conn.closeAll(); - connAux.closeAll(); - - conn = LitebaseConnection.getInstance("Test", "chars_type = unicode; path = " + tempPath); - connAux = LitebaseConnection.getInstance("Test", "chars_type = ascii; path = " + tempPath); - - // The tables are still closed. - assertFalse(conn.isOpen("PERSON1")); - assertFalse(connAux.isOpen("PERSON2")); - assertFalse(connAux.isOpen("PERSON1")); - assertFalse(conn.isOpen("PERSON2")); - - conn.closeAll(); - connAux.closeAll(); - - try // The .dbo of the ascii table must be smaller than the .dbo of the unicode table. - { - File file1, - file2; - assertLower((file1 = new File(tempPath + "Test-person2.dbo", File.READ_WRITE)).getSize(), - (file2 = new File(tempPath + "Test-person1.dbo", File.READ_WRITE)).getSize()); - file1.close(); - file2.close(); - } - catch (IOException exception) {} - } - -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestBigJoins.java b/LitebaseSDK/src/java/samples/sys/testcases/TestBigJoins.java deleted file mode 100644 index 743ffdaa7d..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestBigJoins.java +++ /dev/null @@ -1,987 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.*; -import totalcross.unit.TestCase; -import totalcross.util.Random; - -/** - * Tests joins with big table to ensure that the use indices. - */ -public class TestBigJoins extends TestCase -{ - /** - * The connection with Litebase. - */ - private LitebaseConnection connection = AllTests.getInstance("Test"); - - /** - * Main test method. - */ - public void testRun() - { - String[] tables; - test2Tables(); - test3Tables(); - test4Tables(); - - // Lists all the table names and tests if all tables of this test are in the list. - assertGreaterOrEqual((tables = connection.listAllTables()).length, 9); - hasTableName(tables, "animal"); - hasTableName(tables, "ordenha"); - hasTableName(tables, "precos"); - hasTableName(tables, "produtos"); - hasTableName(tables, "estoque"); - hasTableName(tables, "componenttype"); - hasTableName(tables, "componentdefinition"); - hasTableName(tables, "component"); - hasTableName(tables, "panel"); - - connection.closeAll(); - } - - /** - * Tests a very big join with 2 tables and aggregation. - */ - private void test2Tables() - { - // Drops the tables. - if (connection.exists("animal")) - connection.executeUpdate("drop table animal"); - if (connection.exists("ordenha")) - connection.executeUpdate("drop table ordenha"); - - // Creates the tables. - connection.execute("CREATE TABLE ANIMAL (ID INT, APELIDO CHAR(50) NOT NULL, CODIGO_PROPRIEDADE INT NOT NULL, CODIGO_CLIENTE INT NOT NULL)"); - connection.execute("CREATE TABLE ORDENHA (NUMERO INT NOT NULL, ID_ANIMAL INT NOT NULL, LEITE DOUBLE, DATA_HORA DATETIME)"); - - // Prepared statements. - PreparedStatement psInsertAnimal = connection.prepareStatement("INSERT INTO ANIMAL VALUES (?, ?, ?, ?)"), - psInsertOrdenha = connection.prepareStatement("INSERT INTO ORDENHA VALUES (?, ?, ?, ?)"); - - // Huge inserts. - connection.setRowInc("animal", 10000); - connection.setRowInc("ordenha", 30000); - - int id = 0, - num; - Time time = new Time(); - - // Insert data. - while (++id <= 10000) - { - psInsertAnimal.setInt(0, id); - psInsertAnimal.setString(1, "Apelido "+id); - psInsertAnimal.setInt(2, 1); - psInsertAnimal.setInt(3, 34530); - psInsertAnimal.executeUpdate(); - - num = 0; - while (++num <= 3) - { - time.update(); - psInsertOrdenha.setInt(0, num); - psInsertOrdenha.setInt(1, id); - psInsertOrdenha.setDouble(2, 21.5); - psInsertOrdenha.setDateTime(3, time); - psInsertOrdenha.executeUpdate(); - } - } - - // Stops batch loading. - connection.setRowInc("animal", -1); - connection.setRowInc("ordenha", -1); - - // Adds the primary keys. - connection.executeUpdate("alter table animal add primary key (id)"); - connection.executeUpdate("alter table ordenha add primary key (numero, id_animal)"); - - // Adds the indices. - connection.execute("CREATE INDEX idx on ANIMAL(CODIGO_CLIENTE)"); - connection.execute("CREATE INDEX idx on ANIMAL(CODIGO_PROPRIEDADE)"); - connection.execute("CREATE INDEX idx on ORDENHA(ID_ANIMAL)"); - connection.execute("CREATE INDEX idx on ORDENHA(LEITE)"); - - // Does the best select ordering. - id = Vm.getTimeStamp(); - ResultSet resultSet = connection.executeQuery("SELECT SUM(ORDENHA.LEITE) AS TOTAL FROM ORDENHA, ANIMAL WHERE ORDENHA.LEITE > 0 " - + "AND ANIMAL.CODIGO_CLIENTE = 34530 AND ANIMAL.CODIGO_PROPRIEDADE = 1 AND ANIMAL.ID = ORDENHA.ID_ANIMAL"); - resultSet.first(); - assertEquals(645000.0, resultSet.getDouble(1), 0.0001); - output((Vm.getTimeStamp() - id) + " ms."); - resultSet.close(); - } - - /** - * Tests a join with 3 tables. - */ - private void test3Tables() - { - Random r = new Random(100331); - - // Drops the tables. - if (connection.exists("precos")) - connection.executeUpdate("drop table precos"); - if (connection.exists("produtos")) - connection.executeUpdate("drop table produtos"); - if (connection.exists("estoque")) - connection.executeUpdate("drop table estoque"); - - // Creates the tables. - connection.execute("create table precos (CODPROD char(14), CODCONDPAG char(14), PRCVENDALONG long, codlista char(3))"); - connection.execute("create table produtos (CODPROD char(14), CODGRP char(14), SITUACAO char(14), DESCRPROD char(40), UNIDADE char(14))"); - connection.execute("create table estoque (CODPROD char(14), num int, cod_un_neg char(14))"); - - // Prepared statements. - PreparedStatement psPrecos = connection.prepareStatement("insert into precos values(?, ?, ?, ?)"), - psProdutos = connection.prepareStatement("insert into produtos values(?, ?, ?, ?, ?)"), - psEstoque = connection.prepareStatement("insert into estoque values (?, ?, ?)"); - - // Huge inserts. - connection.setRowInc("precos", 10000); - connection.setRowInc("produtos", 5000); - connection.setRowInc("estoque", 10000); - - int i = -1, - j; - - // Inserts data. - while (++i < 2000) - { - j = -1; - while (++j < 5) - { - if (r.between(0, 1) == 1) - { - psPrecos.setString(0, "nome" + i); - psPrecos.setString(1, "nome" + i); - psPrecos.setLong(2, i + j); - psPrecos.setString(3, "l" + j); - psPrecos.executeUpdate(); - } - psEstoque.setString(0, "nome" + i); - psEstoque.setInt(1, j); - psEstoque.setString(2, "nome" + j); - psEstoque.executeUpdate(); - } - psProdutos.setString(0, "nome" + i); - psProdutos.setString(1, "nome" + i); - psProdutos.setString(2, "nome" + i); - psProdutos.setString(3, "nome" + i); - psProdutos.setString(4, "nome" + i); - psProdutos.executeUpdate(); - } - - // Stops batch loading. - connection.setRowInc("precos", -1); - connection.setRowInc("produtos", -1); - connection.setRowInc("estoque", -1); - - // Creates the indices. - connection.execute("create index idx on precos (CODPROD)"); - connection.execute("create index idx on precos (CODlista)"); - connection.execute("create index idx on produtos (CODPROD)"); - connection.execute("create index idx on estoque (CODPROD)"); - - i = Vm.getTimeStamp(); - j = 0; - - ResultSet resultSet = connection.executeQuery("select * from precos, produtos, estoque where estoque.CODPROD = produtos.CODPROD and produtos.CODPROD = precos.CODPROD and codlista = 'l1'"); - String column2; - - output((Vm.getTimeStamp() - i) + " ms."); - - try // Checks the results. - { - while (resultSet.next()) - { - assertEquals(resultSet.getString(1), column2 = resultSet.getString(2)); - assertEquals(Convert.toLong(column2.substring(4)), resultSet.getLong(3) - 1); - assertEquals("l1", resultSet.getString(4)); - assertEquals(column2, resultSet.getString(5)); - assertEquals(column2, resultSet.getString(6)); - assertEquals(column2, resultSet.getString(7)); - assertEquals(column2, resultSet.getString(8)); - assertEquals(column2, resultSet.getString(9)); - assertEquals(column2, resultSet.getString(10)); - assertEquals(j, resultSet.getInt(11)); - assertEquals(j, Convert.toInt(resultSet.getString(12))); - j = (j + 1) % 5; - } - } - catch (InvalidNumberException exception) {} - - resultSet.close(); - } - - /** - * Tests many joins with 4 tables which answers only one row. - */ - private void test4Tables() - { - // Drops the tables. - if (connection.exists("ComponentType")) - connection.executeUpdate("drop table ComponentType"); - if (connection.exists("ComponentDefinition")) - connection.executeUpdate("drop table ComponentDefinition"); - if (connection.exists("Panel")) - connection.executeUpdate("drop table Panel"); - if (connection.exists("Component")) - connection.executeUpdate("drop table Component"); - - // Creates the tables. - connection.execute("create table ComponentType (id long, name char(20))"); - connection.execute("create table ComponentDefinition (id long, name char(20))"); - connection.execute("create table Panel (id long, name char(20))"); - connection.execute("create table Component (id long, typeid long, componentdefinitionid long, panelid long, name char(20))"); - - // Inserts data into ComponentType. - int j = 0; - PreparedStatement ps = connection.prepareStatement("insert into ComponentType values (?, ?)"); - connection.setRowInc("ComponentType", 4); - while (++j <= 4) - { - ps.setLong(0, j); - ps.setString(1, "Type " + j); - ps.executeUpdate(); - } - connection.setRowInc("ComponentType", -1); - - // Inserts data into ComponentDefinition. - j = 0; - ps = connection.prepareStatement("insert into ComponentDefinition values (?, ?)"); - connection.setRowInc("ComponentDefinition", 40); - while (++j <= 40) - { - ps.setLong(0, j); - ps.setString(1, "Definition " + j); - ps.executeUpdate(); - } - connection.setRowInc("ComponentDefinition", -1); - - // Inserts data into Panel. - j = 0; - ps = connection.prepareStatement("insert into Panel values (?, ?)"); - connection.setRowInc("Panel", 400); - while (++j <= 400) - { - ps.setLong(0, j); - ps.setString(1, "Panel " + j); - ps.executeUpdate(); - } - connection.setRowInc("Panel", -1); - - // Inserts data into Component. - j = 0; - ps = connection.prepareStatement("insert into Component values (?, ?, ?, ?, ?)"); - connection.setRowInc("Component", 4000); - while (++j <= 4000) - { - ps.setLong(0, j); - ps.setLong(1, (j / 1000) + 1); - ps.setLong(2, (j / 100) + 1); - ps.setLong(3, (j / 10) + 1); - ps.setString(4, "Component " + j); - ps.executeUpdate(); - } - connection.setRowInc("Component", -1); - - // Adds the primary keys. - connection.executeUpdate("alter table ComponentType add primary key (id)"); - connection.executeUpdate("alter table ComponentDefinition add primary key (id)"); - connection.executeUpdate("alter table Panel add primary key (id)"); - connection.executeUpdate("alter table Component add primary key (id)"); - - // Creates the indices. - connection.execute("create index typeid on component(typeid)"); - connection.execute("create index compdefid on component(componentdefinitionid)"); - connection.execute("create index panelid on component(panelid)"); - - String baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Component, Panel, ComponentDefinition, " - + "ComponentType where ", - d = "Component.componentdefinitionid = ComponentDefinition.id", - i = "Component.id = 1", - p = "Component.panelid = Panel.id", - t = "Component.typeid = ComponentType.id"; - - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Component, Panel, ComponentType, " - + "ComponentDefinition where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Component, ComponentDefinition, Panel, " - + "ComponentType where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Component, ComponentDefinition, ComponentType, " - + "Panel where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Component, ComponentType, Panel, " - + "ComponentDefinition where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Component, ComponentType, ComponentDefinition, " - + "Panel where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Panel, Component, ComponentDefinition, " - + "ComponentType where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Panel, Component, ComponentType, " - + "ComponentDefinition where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Panel, ComponentDefinition, Component, " - + "ComponentType where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Panel, ComponentDefinition, ComponentType, " - + "Component where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Panel, ComponentType, Component, " - + "ComponentDefinition where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from Panel, ComponentType, ComponentDefinition, " - + "Component where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentDefinition, Component, Panel, " - + "ComponentType where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentDefinition, Component, ComponentType," - + " Panel where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentDefinition, Panel, Component, " - + "ComponentType where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentDefinition, Panel, ComponentType, " - + "Component where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentDefinition, ComponentType, Component," - + " Panel where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentDefinition, ComponentType, Panel, " - + "Component where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentType, Component, Panel, " - + "ComponentDefinition where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentType, Component, ComponentDefinition," - + " Panel where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentType, Panel, Component, " - + "ComponentDefinition where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentType, Panel, ComponentDefinition, Component where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentType, ComponentDefinition, Component," - + " Panel where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - - baseSQL = "select Component.name, ComponentType.name, ComponentDefinition.name, Panel.name from ComponentType, ComponentDefinition, Panel, " - + "Component where "; - executeTestQuery(baseSQL + d + " and " + i + " and " + p + " and " + t); - executeTestQuery(baseSQL + d + " and " + i + " and " + t + " and " + p); - executeTestQuery(baseSQL + d + " and " + p + " and " + i + " and " + t); - executeTestQuery(baseSQL + d + " and " + p + " and " + t + " and " + i); - executeTestQuery(baseSQL + d + " and " + t + " and " + i + " and " + p); - executeTestQuery(baseSQL + d + " and " + t + " and " + p + " and " + i); - executeTestQuery(baseSQL + i + " and " + d + " and " + p + " and " + t); - executeTestQuery(baseSQL + i + " and " + d + " and " + t + " and " + p); - executeTestQuery(baseSQL + i + " and " + p + " and " + d + " and " + t); - executeTestQuery(baseSQL + i + " and " + p + " and " + t + " and " + d); - executeTestQuery(baseSQL + i + " and " + t + " and " + d + " and " + p); - executeTestQuery(baseSQL + i + " and " + t + " and " + p + " and " + d); - executeTestQuery(baseSQL + p + " and " + d + " and " + i + " and " + t); - executeTestQuery(baseSQL + p + " and " + d + " and " + t + " and " + i); - executeTestQuery(baseSQL + p + " and " + i + " and " + d + " and " + t); - executeTestQuery(baseSQL + p + " and " + i + " and " + t + " and " + d); - executeTestQuery(baseSQL + p + " and " + t + " and " + d + " and " + i); - executeTestQuery(baseSQL + p + " and " + t + " and " + i + " and " + d); - executeTestQuery(baseSQL + t + " and " + d + " and " + i + " and " + p); - executeTestQuery(baseSQL + t + " and " + d + " and " + p + " and " + i); - executeTestQuery(baseSQL + t + " and " + i + " and " + d + " and " + p); - executeTestQuery(baseSQL + t + " and " + i + " and " + p + " and " + d); - executeTestQuery(baseSQL + t + " and " + p + " and " + d + " and " + i); - executeTestQuery(baseSQL + t + " and " + p + " and " + i + " and " + d); - } - - /** - * Tests if all queries return only one row and if they are fast enough. - * - * @param sql The string to be tested. - */ - private void executeTestQuery(String sql) - { - int time = Vm.getTimeStamp(); - ResultSet resultSet = connection.executeQuery(sql); - assertEquals(1, resultSet.getRowCount()); - assertGreater(1000, Vm.getTimeStamp() - time); - resultSet.close(); - } - - /** - * Asserts that a given table name is in the table list. - * - * @param array The list of table names. - * @param tableName The table name being searched. - */ - private void hasTableName(String[] array, String tableName) - { - int length = array.length; - - while (--length >= 0) - if (array[length].equals(tableName)) - return; - fail(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestBlob.java b/LitebaseSDK/src/java/samples/sys/testcases/TestBlob.java deleted file mode 100644 index 87f2b40585..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestBlob.java +++ /dev/null @@ -1,475 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.unit.*; -import totalcross.util.Random; - -/** - * Tests the use of blobs. - */ -public class TestBlob extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - byte[][] bytes = new byte[10][10]; - Random rand = new Random(); - int i = 10, - j, - ch = 'a'; - - status("TestBlob: 1"); - - // Drops the tables. - if (driver.exists("blob0")) - driver.executeUpdate("drop table blob0"); - if (driver.exists("blob1")) - driver.executeUpdate("drop table blob1"); - if (driver.exists("blob2")) - driver.executeUpdate("drop table blob2"); - if (driver.exists("blob3")) - driver.executeUpdate("drop table blob3"); - - status("TestBlob: 2"); - - try // Invalid size multiplier. - { - driver.execute("create table blob0 (value blob(10 G))"); - fail("1"); - } catch (SQLParseException exception) {} - - try // Blob too big. - { - driver.execute("create table blob0 (value blob(11 M))"); - fail("2"); - } catch (SQLParseException exception) {} - - // Creates the tables. - driver.execute("create table blob1 (value blob(100) not null)"); - driver.execute("create table blob2 (name varchar(10), picture blob(100 K))"); - driver.execute("create table blob3 (name varchar(10), id int, video blob(1 M))"); - - // Checks that the tables exist. - assertTrue(driver.exists("blob1") && driver.exists("blob2") && driver.exists("blob3")); - - try // A blob can't be in a primary key. - { - driver.execute("create table blob0 (value blob(100) primary key)"); - fail("3"); - } - catch (SQLParseException exception) {} - assertFalse(driver.exists("blob0")); - - try // A blob can't be in a primary key. - { - driver.execute("create table blob0 (value blob(100), primary key(value))"); - fail("4"); - } - catch (SQLParseException exception) {} - assertFalse(driver.exists("blob0")); - - try // A blob can't be in a composed primary key. - { - driver.execute("create table blob0 (value blob(100), age int, primary key(value, age))"); - fail("5"); - } - catch (SQLParseException exception) {} - assertFalse(driver.exists("blob0")); - - try // A blob can't be in a composed primary key. - { - driver.execute("create table blob0 (value blob(100), age int, primary key(age, value))"); - fail("6"); - } - catch (SQLParseException exception) {} - assertFalse(driver.exists("blob0")); - - try // There can't be a default value for blobs. - { - driver.execute("create table blob0 (value blob(100) default null, age int)"); - fail("7"); - } - catch (SQLParseException exception) {} - assertFalse(driver.exists("blob0")); - - try // A blob column can't be indexed. - { - driver.execute("create index idx on blob1(value)"); - fail("8"); - } - catch (SQLParseException exception) {} - - try // A blob column can't be indexed. - { - driver.execute("create index idx on blob1(value, rowid)"); - fail("9"); - } - catch (SQLParseException exception) {} - - try // A blob can't be in a primary key. - { - driver.executeUpdate("alter table blob2 add primary key (picture)"); - fail("10"); - } - catch (SQLParseException exception) {} - - try // A blob can't be in a composed primary key. - { - driver.executeUpdate("alter table blob2 add primary key (name, picture)"); - fail("11"); - } - catch (SQLParseException exception) {} - - status("TestBlob: 3"); - - String path = driver.getSourcePath(); - - try // The index files can't exist because no index was created. - { - assertFalse(new File(path + "Test-blob1$1.idk", File.DONT_OPEN).exists()); - assertFalse(new File(path + "Test-blob1&1.idk", File.DONT_OPEN).exists()); - assertFalse(new File(path + "Test-blob2$1.idk", File.DONT_OPEN).exists()); - assertFalse(new File(path + "Test-blob2&1.idk", File.DONT_OPEN).exists()); - } - catch (IOException exception) {} - - try // A blob can't be in a where clause. - { - driver.executeQuery("select * from blob1 where value > 100"); - fail("12"); - } - catch (SQLParseException exception) {} - - try // A blob can't be in an order / group by clause. - { - driver.executeQuery("select value from blob1 order by value"); - fail("13"); - } - catch (SQLParseException exception) {} - - try // A blob can't be in an order / group by clause. - { - driver.executeQuery("select value from blob1 group by value"); - fail("14"); - } - catch (SQLParseException exception) {} - - try // A blob can only be inserted throught prepared statements. - { - driver.executeUpdate("insert into blob1 values (1)"); - fail("15"); - } - catch (DriverException exception) {} - - try // A blob can only be inserted throught prepared statements. - { - driver.executeUpdate("insert into blob1 values ('a')"); - fail("16"); - } - catch (DriverException exception) {} - - try // A blob can only be updated throught prepared statements. - { - driver.executeUpdate("update blob1 set value = 3 where rowid = 1"); - fail("17"); - } - catch (DriverException exception) {} - - try // A blob can't be in a where clause. - { - driver.prepareStatement("select * from blob1 where value = ?"); - fail("18"); - } - catch (SQLParseException exception) {} - - try // A blob can't be in a where clause. - { - driver.prepareStatement("delete from blob1 where value = ?"); - fail("19"); - } - catch (SQLParseException exception) {} - - status("TestBlob: 4"); - - while (--i >= 0) // Creates random blobs. - { - j = 10; - while (--j >= 0) - bytes[i][j] = (byte)rand.nextInt(128); - } - - // Creates prepared statements for each table. - PreparedStatement preparedStmt1 = driver.prepareStatement("Insert into blob1 values (?)"); - PreparedStatement preparedStmt2 = driver.prepareStatement("insert into blob2 (picture, name) values (?, ?)"); - PreparedStatement preparedStmt3 = driver.prepareStatement("insert into blob3 (id, video, name) values (?, ?, ?)"); - - driver.execute("create index idx on blob2(name)"); - - try // The blob column of this table can't be null. - { - preparedStmt1.setBlob(0, null); - preparedStmt1.executeUpdate(); - fail("20"); - } - catch (DriverException exception) {} - - try // The blob column of this table can't be null. - { - preparedStmt1.setBlob(0, bytes[0]); - preparedStmt1.clearParameters(); - preparedStmt1.executeUpdate(); - fail("21"); - } - catch (DriverException exception) {} - - status("TestBlob: 5"); - - // Populates the tables. - i = -1; - while (++i < 10) - { - preparedStmt1.setBlob(0, bytes[i]); - assertEquals(1, preparedStmt1.executeUpdate()); - - preparedStmt2.setBlob(0, bytes[i]); - preparedStmt2.setString(1, ch + "\'"); - assertEquals(1, preparedStmt2.executeUpdate()); - - preparedStmt3.setBlob(1, bytes[i]); - preparedStmt3.setString(2, "\'" + ch); - preparedStmt3.setInt(0, ch++); - assertEquals(1, preparedStmt3.executeUpdate()); - } - - status("TestBlob: 6"); - - // Queries the tables. - ResultSet rs1 = driver.executeQuery("select * from blob1"); - ResultSet rs2 = driver.executeQuery("select * from blob2"); - ResultSet rs3 = driver.executeQuery("select * from blob3"); - assertEquals(10, rs1.getRowCount()); - assertEquals(10, rs2.getRowCount()); - assertEquals(10, rs3.getRowCount()); - - // Checks that each query is correct. - ch = 'a'; - i = -1; - while (++i < 10) - { - rs1.next(); - rs2.next(); - rs3.next(); - assertEquals(bytes[i], rs1.getBlob("value")); - assertEquals(bytes[i], rs2.getBlob("picture")); - assertEquals(bytes[i], rs3.getBlob("video")); - assertEquals(ch + "\'", rs2.getString("name")); - assertEquals("\'" + ch, rs3.getString("name")); - assertEquals(ch++, rs3.getInt("id")); - assertNull(rs1.getString("value")); - assertNull(rs2.getString("picture")); - assertNull(rs3.getString("video")); - } - rs1.close(); - rs2.close(); - rs3.close(); - - // Tests updates with blobs, writing them in the reverse order. - preparedStmt3 = driver.prepareStatement("update blob3 set video = ? where rowid = ?"); - i = -1; - while (++i < 10) - { - preparedStmt3.setBlob(0, bytes[i]); - preparedStmt3.setInt(1, 10 - i); - assertEquals(1, preparedStmt3.executeUpdate()); - } - - // Checks that the blobs were updated with success. - assertEquals(10, (rs3 = driver.executeQuery("select * from blob3")).getRowCount()); - i = 10; - while (--i >= 0) - { - rs3.next(); - assertEquals(bytes[i], rs3.getBlob("video")); - } - rs3.close(); - - // Creates bigger blobs. - bytes = new byte[10][11]; - i = 10; - while (--i >= 0) - { - j = 10; - while (--j >= 0) - bytes[i][j] = (byte)rand.nextInt(128); - } - - preparedStmt1 = driver.prepareStatement("update blob1 set value = ? where rowid = ?"); - preparedStmt2 = driver.prepareStatement("update blob2 set picture = ? where rowid = ?"); - - status("TestBlob: 7"); - - // Does updates with bigger blobs. - i = -1; - while (++i < 10) - { - preparedStmt1.setBlob(0, bytes[i]); - preparedStmt1.setInt(1, i + 1); - preparedStmt2.setBlob(0, bytes[i]); - preparedStmt2.setInt(1, i + 1); - preparedStmt3.setBlob(0, bytes[i]); - preparedStmt3.setInt(1, i + 1); - assertEquals(1, preparedStmt1.executeUpdate()); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(1, preparedStmt3.executeUpdate()); - } - - // Queries the tables. - assertEquals(10, (rs1 = driver.executeQuery("select value from blob1")).getRowCount()); - assertEquals(10, (rs2 = driver.executeQuery("select picture from blob2")).getRowCount()); - assertEquals(10, (rs3 = driver.executeQuery("select video from blob3")).getRowCount()); - - status("TestBlob: 8"); - - // Checks that each query is correct. - i = -1; - while (++i < 10) - { - rs1.next(); - rs2.next(); - rs3.next(); - assertEquals(bytes[i], rs1.getBlob(1)); - assertEquals(bytes[i], rs2.getBlob(1)); - assertEquals(bytes[i], rs3.getBlob(1)); - } - rs1.close(); - rs2.close(); - rs3.close(); - - status("TestBlob: 9"); - - // Inserts a null in a blob. - (preparedStmt2 = driver.prepareStatement("insert into blob2 (picture, name) values (?, ?)")).setBlob(0, null); - preparedStmt2.setString(1, "Juliana"); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(1, (rs2 = driver.executeQuery("select * from blob2 where rowid = 11")).getRowCount()); - rs2.next(); - assertNull(rs2.getBlob(2)); - rs2.close(); - - // Updates a blob that was null.; - (preparedStmt2 = driver.prepareStatement("update blob2 set picture = ? where rowid = 11")).setBlob(0, bytes[0]); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(1, (rs2 = driver.executeQuery("select * from blob2 where rowid = 11")).getRowCount()); - rs2.next(); - assertNotNull(rs2.getBlob(2)); - assertEquals("Juliana", rs2.getString(1)); - rs2.close(); - - (preparedStmt2 = driver.prepareStatement("insert into blob2 (picture, name) values (?, ?)")).setNull(0); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(1, (rs2 = driver.executeQuery("select * from blob2 where rowid = 12")).getRowCount()); - rs2.next(); - assertNull(rs2.getBlob(2)); - rs2.close(); - - // Inserts a null in a blob without a prepared statement. - assertEquals(1, driver.executeUpdate("insert into blob2 (picture, name) values (null, 0)")); - assertEquals(1, (rs2 = driver.executeQuery("select * from blob2 where rowid = 13")).getRowCount()); - rs2.next(); - assertNull(rs2.getBlob(2)); - rs2.close(); - - status("TestBlob: 10"); - - // Updates a blob with null. - preparedStmt3.setBlob(0, null); - assertEquals(1, preparedStmt3.executeUpdate()); - assertEquals(1, (rs3 = driver.executeQuery("select * from blob3 where rowid = 10")).getRowCount()); - rs3.next(); - assertNull(rs3.getBlob("video")); - rs3.close(); - preparedStmt3.setNull(0); - assertEquals(1, preparedStmt3.executeUpdate()); - assertEquals(1, (rs3 = driver.executeQuery("select * from blob3 where rowid = 10")).getRowCount()); - rs3.next(); - assertNull(rs3.getBlob("video")); - rs3.close(); - - // Updates a blob with null without a prepared statement. - assertEquals(1, driver.executeUpdate("update blob3 set video = null where rowid = 10")); - assertEquals(1, (rs3 = driver.executeQuery("select * from blob3 where rowid = 10")).getRowCount()); - rs3.next(); - assertNull(rs3.getBlob("video")); - rs3.close(); - - (preparedStmt3 = driver.prepareStatement("update blob3 set video = ? where rowid = 10")).setBlob(0, bytes[9]); - assertEquals(1, preparedStmt3.executeUpdate()); - assertEquals(1, (rs3 = driver.executeQuery("select * from blob3 where rowid = 10")).getRowCount()); - rs3.next(); - assertEquals(bytes[9], rs3.getBlob("video")); - rs3.close(); - - status("TestBlob: 11"); - - // Inserts an empty string and an empty blob. - (preparedStmt2 = driver.prepareStatement("insert into blob2 (picture, name) values (?, ?)")).setBlob(0, new byte[0]); - preparedStmt2.setString(1, ""); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(1, (rs2 = driver.executeQuery("select * from blob2 where rowid = 14")).getRowCount()); - rs2.next(); - assertEquals(0, rs2.getChars("name").length); - assertEquals(0, rs2.getBlob("picture").length); - rs2.close(); - - // Updates to an empty string and an empty blob. - (preparedStmt2 = driver.prepareStatement("update blob2 set picture = ?, name = '' where rowid = 1")).setBlob(0, new byte[0]); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(2, (rs2 = driver.executeQuery("select * from blob2 where name = ''")).getRowCount()); - rs2.next(); - assertEquals(0, rs2.getChars("name").length); - assertEquals(0, rs2.getBlob("picture").length); - rs2.next(); - assertEquals(0, rs2.getChars("name").length); - assertEquals(0, rs2.getBlob("picture").length); - rs2.close(); - - status("TestBlob: 12"); - - // juliana@202_3 - // Tests order by with a table with blobs. - assertEquals(10, (rs3 = driver.executeQuery("select * from blob3 order by id desc")).getRowCount()); - i = 9; - ch = 'i'; - rs3.next(); - while (--i >= 10) - { - rs3.next(); - assertEquals("\'" + ch, rs3.getString(1)); - assertEquals(ch--, rs3.getInt(2)); - assertEquals(bytes[i], rs3.getBlob(3)); - - } - rs3.close(); - - // Tests that a blob bigger than its definition is trimmed. - preparedStmt2.setBlob(0, new byte[102401]); - assertEquals(1, preparedStmt2.executeUpdate()); - assertEquals(1, (rs2 = driver.executeQuery("select * from blob2 where rowid = 1")).getRowCount()); - rs2.next(); - assertEquals(0, rs2.getChars("name").length); - assertEquals(102400, rs2.getBlob("picture").length); - assertEquals("\t", rs2.rowToString()); - rs2.close(); - - driver.closeAll(); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestCachedRows.java b/LitebaseSDK/src/java/samples/sys/testcases/TestCachedRows.java deleted file mode 100644 index fe0ac6875d..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestCachedRows.java +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests sequences of inserts and deletes - */ -public class TestCachedRows extends TestCase // Catchs error found in 555_3 -{ - /** - * The main method of the test. - */ - public void testRun() - { - String[] querysSQL = - { - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 38", - "INSERT INTO ACT_CLIENTE VALUES (38L, '38', 'ADENILZA xxxxxxxxxxxxxxxxxxxxxE', 'ADENILZA 111111111111111111111E', '11.111.222//4443-22', " - + "'', 1, 1, '', 4, '3423423421', '', '', '1', 20051110101123, 20051110101123, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 114L", - "INSERT INTO ACT_CLIENTE VALUES (114L, '114', 'NIETO yyyyyyyyyyyyyyyyyyyyE', 'NIETO 2222222222222222222EE', '22.222.333//3333-33', '', 1, " - + "1, '', 4, '4342342423', '', '', '1', 20051110101123, 20051110101123, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 161L", - "INSERT INTO ACT_CLIENTE VALUES (161L, '161', 'ANTONIO bbbbbbbbbbbbbbbbbbbbbbE', 'ANTONIO 33333333333333333333333', '44.444.444//4441-44', " - + "'', 1, 1, '', 4, '5435656458', '', '', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 421L", - "INSERT INTO ACT_CLIENTE VALUES (421L, '421', 'CLAUDIOMIR cccccccccccccccccMEE', 'CLAUDIOMIR 444444444444444444EE', '55.555.555//5555-26', " - + "'', 1, 1, '', 4, '', '', '', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 443L", - "INSERT INTO ACT_CLIENTE VALUES (443L, '443', 'MARIA dddddddddddddddddSO', 'MARIA 55555555555555555SO', '777.777.777-20', '', 1, 1, '', 4, " - + "'6756756756', '', '', '2', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 941L", - "INSERT INTO ACT_CLIENTE VALUES (941L, '941', 'J.B.eeeeeeeeeeeeeeeeeeeeeeeeeeE', 'J.B.6666666666666666666666666EE', '88.888.888//8889-83', " - + "'', 1, 1, '', 4, '8655676565', '', '', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 968L", - "INSERT INTO ACT_CLIENTE VALUES (968L, '968', 'JARDELIO fffffffffffffffffffffffE', 'JARDELIO 7777777777777777777777EE'," - + " '99.999.999//9999-82', '', 1, 1, '', 4, '7656456547', '', '', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1217L", - "INSERT INTO ACT_CLIENTE VALUES (1217L, '1217', 'N.gggggggggggggggE', 'N.C.8888888888888E', '00.000.000//1111-16', '', 1, 1, '', 4, " - + "'4532432439', '', '', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1450L", - "INSERT INTO ACT_CLIENTE VALUES (1450L, '1450', 'OTTO COMhhhhhhhhhhhhhhhhhhhhhhhE', 'OTTO 9999999999999999999999999EE', " - + "'22.222.222//2222-17', '', 1, 1, '', 4, '4535345340', '', '', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1585L", - "INSERT INTO ACT_CLIENTE VALUES (1585L, '1585', 'OROSINO iiiiiiiiiiiiiiiiiiiE', 'OROSINO 0000000000000000000E', " - + "'33.333.333//3333-33', '', 1, 1, '', 4, '5435345357', '', '30 ', '1', 20051110101124, 20051110101124, 1)", - "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1664L" - }; - - LitebaseConnection driver = AllTests.getInstance("Test"); - int numSQL = querysSQL.length; - - if (driver.exists("act_cliente")) driver.executeUpdate("drop table act_cliente"); - driver.execute("create table act_cliente (actclienteid long, actcodcliente char(30), actrazaosocial char(100), " - + "actnomefantasia char(100), actie char(20), actcnpj char(20), actstatus long, acttipocliente long, actmail char(255), " - + "actvendedorid long, acttelefone char(18), actobservacao char(255), actfax char(18), actcodtabela char(20), " - + "actdatinclusao long, actdatalteracao long, actusuarioid long)"); - - int i = -1; - while (++i < numSQL) - assertEquals(i & 1, driver.executeUpdate(querysSQL[i])); // Delete must return 0; insert must return 1. - - driver.closeAll(); - - // rnovais@570_77 - ResultSet rs = (driver = AllTests.getInstance("Test")) - .executeQuery("select actrazaosocial from act_cliente where actrazaosocial='ADENILZA xxxxxxxxxxxxxxxxxxxxxE'"); - assertEquals(1, rs.getRowCount()); // Would be 6 without 555_3 fix. - rs.close(); - assertEquals(10, (rs = driver.executeQuery("select * from act_cliente")).getRowCount()); - rs.close(); - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestClosedLitebaseAndProcessLogs.java b/LitebaseSDK/src/java/samples/sys/testcases/TestClosedLitebaseAndProcessLogs.java deleted file mode 100644 index a53b4dc21f..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestClosedLitebaseAndProcessLogs.java +++ /dev/null @@ -1,569 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.Time; -import totalcross.sys.Vm; -import totalcross.unit.*; -import totalcross.util.Date; - -/** - * Tests What happens when the driver is closed and tests processLogs(). - */ -public class TestClosedLitebaseAndProcessLogs extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - // Tests prepared statement methods with the driver closed. - if (driver.exists("tabps")) - driver.executeUpdate("drop table tabps"); - driver.execute("create table tabps (name char(30))"); - PreparedStatement psIns = driver.prepareStatement("insert into tabps values ('?')"); - PreparedStatement psSel = driver.prepareStatement("select * from tabps where name = ?"); - - psIns.setString(0, "vera"); - psIns.executeUpdate(); - psSel.setString(0, "vera"); - ResultSet resultSet = psSel.executeQuery(); - ResultSetMetaData rsMetaData = resultSet.getResultSetMetaData(); - - driver.closeAll(); - - // All method calls must fail. - - // PreparedStatement methods. - try - { - psSel.setString(0, "vera"); - fail("1"); - } - catch (IllegalStateException exception) {} - try - { - psSel.executeQuery(); - fail("2"); - } - catch (IllegalStateException exception) {} - try - { - psIns.clearParameters(); - fail("3"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setBlob(0, null); - fail("4"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setDate(0, new Date()); - fail("5"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setDateTime(0, (Date)new Date()); - fail("6"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setDateTime(0, (Time)new Time()); - fail("7"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setDouble(0, 0); - fail("8"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setInt(0, 0); - fail("9"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setLong(0, 0); - fail("10"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setNull(0); - fail("11"); - } - catch (IllegalStateException exception) {} - try - { - psIns.setString(0, null); - fail("12"); - } - catch (IllegalStateException exception) {} - try - { - psIns.executeUpdate(); - fail("13"); - } - catch (IllegalStateException exception) {} - - // LitebaseConnection methods. - try - { - driver.convert("tabps"); - fail("14"); - } - catch (IllegalStateException exception) {} - try - { - driver.closeAll(); - fail("15"); - } - catch (IllegalStateException exception) {} - try - { - driver.execute(""); - fail("16"); - } - catch (IllegalStateException exception) {} - try - { - driver.executeQuery(""); - fail("17"); - } - catch (IllegalStateException exception) {} - try - { - driver.executeUpdate(""); - fail("18"); - } - catch (IllegalStateException exception) {} - try - { - driver.exists("tabps"); - fail("19"); - } - catch (IllegalStateException exception) {} - try - { - driver.getCurrentRowId("tabps"); - fail("20"); - } - catch (IllegalStateException exception) {} - try - { - driver.getRowCount("tabps"); - fail("21"); - } - catch (IllegalStateException exception) {} - try - { - driver.getRowCountDeleted("tabps"); - fail("22"); - } - catch (IllegalStateException exception) {} - try - { - driver.getRowIterator("tabps"); - fail("23"); - } - catch (IllegalStateException exception) {} - try - { - driver.prepareStatement(""); - fail("24"); - } - catch (IllegalStateException exception) {} - try - { - driver.purge("tabps"); - fail("25"); - } - catch (IllegalStateException exception) {} - try - { - driver.recoverTable("tabps"); - fail("26"); - } - catch (IllegalStateException exception) {} - try - { - driver.setRowInc("tabps", 100); - fail("27"); - } - catch (IllegalStateException exception) {} - try - { - driver.isOpen("tabps"); - fail("28"); - } - catch (IllegalStateException exception) {} - - // ResultSet methods. - try - { - resultSet.absolute(0); - fail("29"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.afterLast(); - fail("30"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.beforeFirst(); - fail("31"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.first(); - fail("32"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getBlob(0); - fail("33"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getBlob("name"); - fail("34"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getChars(0); - fail("35"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getChars("name"); - fail("36"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getDate(0); - fail("37"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getDate("name"); - fail("38"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getDateTime(0); - fail("39"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getDateTime("name"); - fail("40"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getDouble(0); - fail("41"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getDouble("name"); - fail("42"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getFloat(0); - fail("43"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getFloat("name"); - fail("44"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getInt(0); - fail("45"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getInt("name"); - fail("46"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getLong(0); - fail("47"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getLong("name"); - fail("48"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getResultSetMetaData(); - fail("49"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getRow(); - fail("50"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getRowCount(); - fail("51"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getShort(0); - fail("52"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getShort("name"); - fail("53"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getString(0); - fail("54"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getString("name"); - fail("55"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getStrings(); - fail("56"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getStrings(-1); - fail("57"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.isNull(0); - fail("58"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.isNull("name"); - fail("59"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.last(); - fail("60"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.next(); - fail("61"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.prev(); - fail("62"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.relative(0); - fail("63"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.setDecimalPlaces(0, 2); - fail("64"); - } - catch (IllegalStateException exception) {} - - // ResultSetMetaData methods. - try - { - rsMetaData.getColumnCount(); - fail("65"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getColumnDisplaySize(0); - fail("66"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getColumnLabel(0); - fail("67"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getColumnTableName(0); - fail("68"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getColumnTableName("name"); - fail("69"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getColumnType(0); - fail("70"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getColumnTypeName(0); - fail("71"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.hasDefaultValue(0); - fail("72"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.hasDefaultValue("name"); - fail("73"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.isNotNull(0); - fail("74"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.isNotNull("name"); - fail("75"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getPKColumnIndices("name"); - fail("76"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getPKColumnNames("name"); - fail("77"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getDefaultValue(0); - fail("78"); - } - catch (IllegalStateException exception) {} - try - { - rsMetaData.getDefaultValue("name"); - fail("79"); - } - catch (IllegalStateException exception) {} - - driver = null; - resultSet.close(); - Vm.gc(); - - if ((driver = AllTests.getInstance("Test")).exists("PRODUTO")) - driver.executeUpdate("drop table produto"); - - // guich@566_31: another case that was causing a "Memory error". - // Tets the logging processing. - String[] sql = - { - "new LitebaseConnection(Test,null)", - "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20), " - + "DESCRICAO char(100), UNDCAIXA char(10), PESO float, UNIDADEMEDIDA char(3), EMBALAGEM char(10), PORCTROCA float, PERMITETROCA int)", - "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)", - "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)", - "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)", - "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)", - "closeAll", - "new LitebaseConnection(Test,null)", - }; - driver.closeAll(); - assertNotNull(driver = LitebaseConnection.processLogs(sql, null, false)); - assertEquals(1, driver - .executeUpdate("insert into PRODUTO values(1, '19132',2, 1, '1, 2, 3', 'ABSORVENTE SILHO ABAS', '5', 13, 'PCT', '20X30', 10.f, 0)")); - - // Tests if gc won't make the Litebase instance close. - int i = 100; - while (--i >= 0) - { - testTemporaryConnection(); // Gets another Litebase instance. - driver.exists("tableName"); // The connection can't be closed because of the garbage collector. - } - driver.closeAll(); - - } - - /** - * Gets the current Litebase Instance and does nothing with that. - */ - private void testTemporaryConnection() - { - AllTests.getInstance("Test"); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestComposedIndexAndPK.java b/LitebaseSDK/src/java/samples/sys/testcases/TestComposedIndexAndPK.java deleted file mode 100644 index e38b90fb0b..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestComposedIndexAndPK.java +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.Convert; -import totalcross.unit.*; -import totalcross.io.*; - -/** - * Tests the composed Index and composed primary key. - */ -public class TestComposedIndexAndPK extends TestCase -{ - /** - * The connection with Litebase. - */ - LitebaseConnection driver = AllTests.getInstance("Test"); - - /** - * The path where table files are stored. - */ - String path = driver.getSourcePath(); - - /** - * The main method of the test. - */ - public void testRun() - { - try - { - // Creates and populates a table with simple indices. - if (driver.exists("person")) - driver.executeUpdate("DROP TABLE person"); - driver.execute("CREATE TABLE person(id int, name char(60) NOCASE, address char (100), cod short)"); - driver.execute("CREATE INDEX IDX ON person(namE)"); - driver.execute("CREATE INDEX IDX ON person(id)"); - driver.execute("CREATE INDEX IDX ON person(rowid)"); - driver.executeUpdate("INSERT INTO person VALUES (1, 'Renato Novais', 'Rio de Janeiro', 120)"); - driver.executeUpdate("INSERT INTO person VALUES (2, 'Indira', 'Jequi�', 562)"); - driver.executeUpdate("INSERT INTO person VALUES (3, 'Zenes e Jener', 'Cacul�', 120)"); - driver.executeUpdate("INSERT INTO person VALUES (4, 'Jener e Zenes', 'Cacul�', 500)"); - driver.executeUpdate("INSERT INTO person VALUES (5, 'Lucas/Felipe', 'VCQ/Guanambi', 1001)"); - driver.executeUpdate("INSERT INTO person VALUES (6, 'Danilo/Carol', 'SSA/CLE', 1002)"); - driver.executeUpdate("INSERT INTO person VALUES (7, 'Joao Pedro', 'Cacul�', 620)"); - driver.executeUpdate("INSERT INTO person VALUES (8, 'Neide', 'Salvador', 10)"); - driver.executeUpdate("INSERT INTO person VALUES (9, 'Caio', 'Salvador', 19)"); - driver.executeUpdate("INSERT INTO person VALUES (10, 'Z� de Jener', 'Cacul�', 1234)"); - driver.executeUpdate("INSERT INTO person VALUES (11, 'Marlene', 'Cacul�', 156)"); - driver.executeUpdate("INSERT INTO person VALUES (12, 'Nana', 'Cacul�', 2015)"); - - // Tests that there are no composed index files. - assertFalse(new File(path + "Test-person&1.idk", File.DONT_OPEN).exists()); - - // Creates a composed index. - driver.execute("create index idx on person (id, name)"); - - driver.closeAll(); - assertTrue(new File(path + "Test-person&1.idk", File.DONT_OPEN).exists()); // The composed index files must exist. - - // Deletes a simple index file and opens the table again to re-create it. - new File(path + "Test-person$1.idk", File.READ_WRITE).delete(); - (driver = AllTests.getInstance("Test")).executeQuery("select * from person").close(); - driver.closeAll(); - assertTrue(new File(path + "Test-person$1.idk", File.DONT_OPEN).exists()); // The simple index files must exist. - - // Deletes a composed index file and opens the table again to re-create it. - new File(path + "Test-person&1.idk", File.READ_WRITE).delete(); - (driver = AllTests.getInstance("Test")).executeQuery("select * from person").close(); - assertTrue(new File(path + "Test-person&1.idk", File.DONT_OPEN).exists()); // The composed index files must exist. - - try // Tries to create the composed index again. - { - driver.execute("create index idx on person (id, name)"); - fail("1"); - } - catch (AlreadyCreatedException exception) {} - - // Creates other composed index and checks if their files exist. - driver.execute("create index idx on person (id, address)"); - driver.execute("create index idx on person (cod, name, rowid)"); - driver.execute("create index idx on person (cod, rowid)"); - driver.execute("create index idx on person (rowid, cod)"); - new File(path + "Test-person&2.idk", File.DONT_OPEN).exists(); - new File(path + "Test-person&3.idk", File.DONT_OPEN).exists(); - new File(path + "Test-person&4.idk", File.DONT_OPEN).exists(); - new File(path + "Test-person&5.idk", File.DONT_OPEN).exists(); - - // Drops a composed index and checks that its file does not exist anymore. - driver.executeUpdate("drop index id, name on person"); - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); - - try // Tries to drop it again. - { - driver.executeUpdate("drop index id, name on person"); - fail("2"); - } - catch (DriverException exception) {} - - driver.closeAll(); - - // The other indices still exist. - new File(path + "Test-person&2.idk", File.DONT_OPEN).exists(); - new File(path + "Test-person&3.idk", File.DONT_OPEN).exists(); - - // Creates the dropped index again and checks that it exists again. - (driver = AllTests.getInstance("Test")).execute("create index idx on person (id, name)"); - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); - - // Closes the driver and drop the main table files. - driver.closeAll(); - new File(Convert.appendPath(path, "Test-person.db"), File.READ_WRITE).delete(); - new File(Convert.appendPath(path, "Test-person.dbo"), File.READ_WRITE).delete(); - - // Tests rowid in the composed primary key. - (driver = AllTests.getInstance("Test")).execute("create table person(id int, primary key(rowid))"); - driver.executeUpdate("drop table person"); - driver.execute("create table person(id int, primary key(rowid, id))"); - driver.executeUpdate("drop table person"); - driver.execute("create table person(id int, primary key(id, rowid))"); - driver.executeUpdate("drop table person"); - - try // The composed primary key can't have repeated fields. - { - driver.execute("create table person(id int, name char(10), address char(20), age int, years int, primary key (name, name))"); - fail("3"); - } - catch (SQLParseException exception) {} - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); - - try // The composed primary key can't have repeated fields. - { - driver.execute("create table person(id int, name char(10), address char(20), age int, years int, primary key (rowid, rowid))"); - fail("4"); - } - catch (SQLParseException exception) {} - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); - - try // The composed primary key can't have repeated fields. - { - driver.execute("create table person(id int, name char(10), address char(20), age int, years int, primary key (name, id, name))"); - fail("5"); - } - catch (SQLParseException exception) {} - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); - - // Creates and populates a table with a composed primary key. - driver.execute("create table person(id int, name char(10), address char(20), age int, years int, primary key (id, name))"); - driver.execute("create index idx on person(years)"); - driver.executeUpdate("Insert into person values (1,'Maria', 'Rio de Janeiro', 30, 1)"); - driver.executeUpdate("Insert into person values (3,'RLN', 'Salvador', 44, 3)"); - - try // Repeated key. - { - driver.executeUpdate("Insert into person values (1,'Maria', 'Juazeiro', 50, 6)"); - fail("6"); - } - catch (PrimaryKeyViolationException exception) {} - try // Repeated key. - { - driver.executeUpdate("Insert into person values (3, 'RLN', 'Cacul�', 25, 10)"); - fail("7"); - } - catch (PrimaryKeyViolationException exception) {} - - driver.executeUpdate("Insert into person values (1, 'Indira', 'Jequi�', 21, 14)"); - driver.executeUpdate("Insert into person values (3, 'RLN2', 'piracicaba', 14, 122)"); - - try // Repeated key. - { - driver.executeUpdate("Insert into person values (1, 'Maria', 'jacar�', 26, 16)"); - fail("8"); - } - catch (PrimaryKeyViolationException exception) {} - try // Null in a key. - { - driver.executeUpdate("Insert into person values (1, null, 'jacar�', 26, 16)"); - fail("9"); - } - catch (DriverException exception) {} - try // Null in a key. - { - driver.executeUpdate("Insert into person values (null, 'Maria', 'jacar�', 26, 16)"); - fail("10"); - } - catch (DriverException exception) {} - - driver.executeUpdate("Insert into person values (3, 'RLN1', 'da u p�', 81,166)"); - - // Tests where clause with constants. - ResultSet rs = driver.executeQuery("select * from person where (1 = 1)"); - assertEquals(5, rs.getRowCount()); - rs.close(); - assertEquals(5, (rs = driver.executeQuery("select * from person where not ('a' != 'a')")).getRowCount()); - rs.close(); - assertEquals(0, (rs = driver.executeQuery("select * from person where ('2010/11/23' != '2010/11/23')")).getRowCount()); - rs.close(); - assertEquals(0, (rs = driver.executeQuery("select * from person where not ('2010/11/23 17:28:11' = '2010/11/23 17:28:11')")).getRowCount()); - rs.close(); - assertEquals(0, (rs = driver.executeQuery("select * from person where (0.1d = 0.2d)")).getRowCount()); - rs.close(); - assertEquals(5, (rs = driver.executeQuery("select * from person where not (0 = 1)")).getRowCount()); - rs.close(); - assertEquals(5, (rs = driver.executeQuery("select * from person where ('2010/11/24' != '2010/11/23')")).getRowCount()); - rs.close(); - assertEquals(5, (rs = driver.executeQuery("select * from person where not ('2010/11/23 17:28:11' = '2010/11/23 17:28:12')")).getRowCount()); - rs.close(); - - // Tests queries that uses the composed primary key. - assertEquals(1, (rs = driver.executeQuery("select * from person where (1 = 1 and id = 3 and name = 'RLN')")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where (name = 'RLN' and id = 3 and 1 = 1)")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - // Tests queries that do not use the composed primary key. - assertEquals(1, (rs = driver.executeQuery("select * from person where (1 = 1 and id = 3 and address = 'Salvador')")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where (address = 'Salvador' and id = 3 and 1 = 1)")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(3, (rs = driver.executeQuery("select * from person where (1 = 1 and id = 3)")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(3, (rs = driver.executeQuery("select * from person where (id = 3 and 1 = 1)")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where (1 = 1 and name = 'RLN')")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where (name = 'RLN' and 1 = 1)")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where (address = 'Salvador' and age = 44)")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where age < 45 and age > 43")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where years >= 3 and years <= 3")).getRowCount()); - rs.next(); - assertEquals(3, rs.getInt("id")); - assertEquals("RLN", rs.getString("name")); - assertEquals(44, rs.getInt("age")); - assertEquals("Salvador", rs.getString("address")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); // Tests if the index files exist. - - // Drops the index and checks that the files do not exist anymore. - driver.executeUpdate("ALTER TABLE person DROP primary key"); - new File(path + "Test-person&1.idk", File.DONT_OPEN).exists(); - - driver.executeUpdate("Insert into person values (1,'Maria', 'jacar�', 26, 16)"); - - try // Tries to drop again. - { - driver.executeUpdate("ALTER TABLE person DROP primary key"); - fail("11"); - } - catch (DriverException exception) {} - try // Tries to add. It will fail because of a repeated column name. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, id)"); - fail("12"); - } - catch (SQLParseException exception) {} - try // Tries to add. It will fail because of a repeated column name. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name, id)"); - fail("13"); - } - catch (SQLParseException exception) {} - try // Tries to add an index. It will fail because of a repeated column name. - { - driver.execute("create index idx on person(id, id)"); - fail("14"); - } - catch (SQLParseException exception) {} - try // Tries to add an index. It will fail because of a repeated column name. - { - driver.execute("create index idx on person(id, name, id)"); - fail("15"); - } - catch (SQLParseException exception) {} - try // Tries to add. It will fail because of a repeated key. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - fail("16"); - } - catch (PrimaryKeyViolationException exception) {} - - // Tries again after closing the driver. - driver.closeAll(); - driver = AllTests.getInstance("Test"); - try // Tries to add. It will fail because of a repeated key. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - fail("17"); - } - catch (PrimaryKeyViolationException exception) {} - - // Deletes the repeated key. - assertEquals(1, driver.executeUpdate("delete from person where name = 'Maria' and age = 26")); - - // Now the composed primary key is added with success. - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - assertTrue(new File(path + "Test-person&1.idk", File.DONT_OPEN).exists()); - - // Tests composed PK violations for null inside them. - driver.executeUpdate("ALTER TABLE person DROP primary key"); - driver.executeUpdate("Insert into person values (4, null, 'Rio de Janeiro', 29, 1)"); - - try // Tries to add. It will fail because of a null in the key. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - fail("18"); - } - catch (DriverException exception) {} - - assertEquals(1, driver.executeUpdate("delete from person where id = 4 and age = 29")); - - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - driver.executeUpdate("ALTER TABLE person DROP primary key"); - driver.executeUpdate("Insert into person values (null, 'Juliana', 'Rio de Janeiro', 29, 1)"); - - try // Tries to add. It will fail because of a null in the key. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - fail("19"); - } - catch (DriverException exception) {} - - assertEquals(1, driver.executeUpdate("delete from person where name = 'Juliana' and age = 29")); - - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, name)"); - - // Tests delete; - assertEquals(2, (rs = driver.executeQuery("select * from person where (id = 1)")).getRowCount()); - rs.close(); - driver.executeUpdate("DELETE from person where id = 1 and name = 'Maria'"); - assertEquals(1, (rs = driver.executeQuery("select * from person where (id = 1)")).getRowCount()); - rs.close(); - driver.executeUpdate("DELETE from person where id = 1"); - assertEquals(0, (rs = driver.executeQuery("select * from person where (id = 1)")).getRowCount()); - rs.close(); - - // Tests update. - assertEquals(1, driver.executeUpdate("update person set name = 'juliana' where id = 3 and name = 'RLN'")); - assertEquals(0, (rs = driver.executeQuery("select * from person where id = 3 and name = 'RLN'")).getRowCount()); - rs.close(); - assertEquals(1, (rs = driver.executeQuery("select * from person where id = 3 and name = 'juliana'")).getRowCount()); - rs.close(); - - // Tests purge. - assertEquals(5, driver.purge("person")); - assertEquals(1, (rs = driver.executeQuery("select * from person where id = 3 and name = 'juliana'")).getRowCount()); - rs.close(); - - try // Creates a duplicated composed PK. - { - assertEquals(1, driver.executeUpdate("update person set name = 'maria' where id = 3")); - fail("20"); - } - catch (PrimaryKeyViolationException exception) {} - - // Checks that no row was updated. - assertEquals(1, (rs = driver.executeQuery("select * from person where id = 3 and name = 'maria'")).getRowCount()); - rs.close(); - - // Tests rowid in the composed primary key. - driver.executeUpdate("ALTER TABLE person DROP PRIMARY KEY"); - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(rowid)"); - driver.executeUpdate("ALTER TABLE person DROP PRIMARY KEY"); - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(rowid, id)"); - driver.executeUpdate("ALTER TABLE person DROP PRIMARY KEY"); - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(id, rowid)"); - driver.executeUpdate("ALTER TABLE person DROP PRIMARY KEY"); - - try // Repeated field in the primary key. - { - driver.executeUpdate("ALTER TABLE person ADD PRIMARY KEY(rowid, rowid)"); - fail("21"); - } - catch (SQLParseException exception) {} - - // Tests a table with too many columns and composed primary keys. - if (driver.exists("ITENSPEDIDOS")) - driver.executeUpdate("drop table ITENSPEDIDOS"); - - driver.execute("create table ITENSPEDIDOS (NUMPED long, NUMITEM long, ICODCLI char(5), ICODVEND char(3), CODCONDPAG char(2), " -+ "CODPROD char(14), DATPED datetime, PESOPADRLONG long, PESOPADRDEC short, PRCVENDALONG long, PRCVENDADEC short, PRCVENDIDOLONG long, " -+ "PRCVENDIDODEC short, VALTOTITEMLONG long, VALTOTITEMDEC short, QTDPEDLONG long, QTDPEDDEC short, VALTOTPRCCUSTOLONG long, " -+ "VALTOTPRCCUSTODEC short, PERCLUCROLONG long, PERCLUCRODEC short, PODTROC char(1), COMPL char(1), ENV char(1), REENV char(1), CODEMP varchar(5), " -+ "primary key (NUMPED, ICODCLI, ICODVEND, CODEMP))"); - - driver.closeAll(); - (driver = AllTests.getInstance("Test")).executeQuery("select * from ITENSPEDIDOS").close(); - - if (driver.exists("CDAIT")) - driver.executeUpdate("drop table CDAIT"); - driver.execute("CREATE TABLE CDAIT (CDAITORGAO short NOT NULL, CDAITCOD char(11) nocase NOT NULL, CDAITDIGITO char(1) NOT NULL, " - + "CDAITDATAINFRACAO date, CDAITPLACA char(10) nocase, CDAITLOGRADOURO short, " - + "CDAITINFRACAO char(5), CDAITDESDOBRAMENTO short, CDAITAGENTE char(12) nocase, CDAITDATAENVIO date)"); - driver.execute("CREATE INDEX idx_CDAITAG ON CDAIT(CDAITORGAO, CDAITAGENTE)"); - driver.execute("CREATE INDEX idx_CDAITLOGRAD ON CDAIT(CDAITORGAO, CDAITLOGRADOURO)"); - driver.execute("CREATE INDEX idx_CDAIT ON CDAIT(CDAITORGAO, CDAITCOD, CDAITDIGITO)"); - driver.execute("CREATE INDEX idx_CDAITINFRACAO ON CDAIT(CDAITORGAO, CDAITINFRACAO, CDAITDESDOBRAMENTO, CDAITAGENTE)"); - driver.executeUpdate("insert into CDAIT values (7667, 'EHYYSSSSS�', 'Y', '2011/12/22', 'YYYY:::', 1, 'HX�YH', 1, 'YB�_HE', null)"); - driver.executeUpdate("insert into CDAIT values (7667, 'EHYYSSSSS�', 'Y', '2011/12/22', 'YYYY:::', 1, 'HX�YH', 1, 'YB�_HE', null)"); - assertEquals(2, (rs = driver.executeQuery("SELECT * from CDAIT WHERE CDAITORGAO = 7667 AND CDAITAGENTE = 'YB�_HE' AND CDAITCOD IS NOT NULL")).getRowCount()); - rs.close(); - driver.closeAll(); - - } - catch (IOException exception) - { - exception.printStackTrace(); - fail("22"); - - } - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestCryptoTables.java b/LitebaseSDK/src/java/samples/sys/testcases/TestCryptoTables.java deleted file mode 100644 index ced3260494..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestCryptoTables.java +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import totalcross.sys.*; -import totalcross.unit.TestCase; -import litebase.*; - -/** - * Does tests with tables which use or not cryptography. - */ -public class TestCryptoTables extends TestCase -{ - /** - * Main test method. - */ - public void testRun() - { - String tempPath = Convert.appendPath(Settings.appPath, "temp/"); // The path used. - - // Creates a table within an ascii connection. - LitebaseConnection conn = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - - // The table does not exist yet. - assertFalse(conn.isOpen("person")); - - if (conn.exists("person")) - conn.executeUpdate("drop table person"); - conn.execute("CREATE table PERSON (NAME CHAR(10))"); - assertTrue(conn.isOpen("person")); - conn.closeAll(); - - // Trying to use this table in an unicode connection must fail. - conn = LitebaseConnection.getInstance("Test", tempPath); - try - { - conn.executeUpdate("insert into person values ('Juliana')"); - fail("1"); - } - catch (DriverException exception) {} - conn.executeUpdate("drop table person"); - conn.closeAll(); - - // Creates a table within an unicode connection. - (conn = LitebaseConnection.getInstance("Test", "path = " + tempPath)).execute("create table person (name char(10))"); - conn.closeAll(); - - // Trying to use this table in an ascii connection must fail. - conn = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - try - { - conn.execute("create index idx on person(name)"); - fail("2"); - } - catch (DriverException exception) {} - conn.executeUpdate("drop table person"); - conn.closeAll(); - - // Creates two connections which only differ in the string types. They must be different. - LitebaseConnection connAux = LitebaseConnection.getInstance("Test", "path = " + tempPath + " ; crypto"); - assertNotEquals(conn = LitebaseConnection.getInstance("Test", "path = " + tempPath), connAux); - - // Since the paths and the application id are the same, the second connection can't create the table. - conn.execute("create table person (name char(10))"); - - conn.closeAll(); - try - { - connAux.execute("create table person (name char(10))"); - fail("3"); - } - catch (AlreadyCreatedException exception) {} - - try // Trying to use an unicode table in an ascii connection must fail. - { - connAux.executeQuery("select * from person"); - fail("4"); - } - catch (DriverException exception) {} - connAux.closeAll(); - - // Trying to use an unicode table in an ascii connection must fail, but it can't fail in an unicode connection. - conn = LitebaseConnection.getInstance("Test", "path = " + tempPath); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - try - { - connAux.executeQuery("select * from person"); - fail("5"); - } - catch (DriverException exception) {} - assertNotNull(conn.executeQuery("select * from person")); - conn.executeUpdate("drop table person"); - conn.closeAll(); - connAux.closeAll(); - - // Creates and populates an ascii and an unicode table. - conn = LitebaseConnection.getInstance("Test", "path = " + tempPath); - if (conn.exists("person1")) - conn.executeUpdate("drop table person1"); - conn.execute("create table person1 (name char(10) primary key)"); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - if (connAux.exists("person2")) - connAux.executeUpdate("drop table person2"); - connAux.execute("create table person2 (name char(10) primary key)"); - - int i = -1; - while (++i < 100) - { - conn.executeUpdate("insert into person1 values ('Name" + i + "')"); - connAux.executeUpdate("insert into person2 values ('Name" + i + "')"); - } - - // It is not possible to use a table within a connection with a different kind of string. - conn.executeUpdate("insert into person1 values ('Name100')"); - conn.closeAll(); - try - { - connAux.executeUpdate("insert into person1 values ('Name100')"); - fail("6"); - } - catch (DriverException exception) {} - connAux.executeUpdate("insert into person2 values ('Name100')"); - connAux.closeAll(); - conn = LitebaseConnection.getInstance("Test", "path = " + tempPath); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - try - { - - conn.executeUpdate("insert into person2 values ('Name100')"); - fail("7"); - } - catch (DriverException exception) {} - - // Asserts that the elements were saved correctly. - ResultSet rs1 = conn.executeQuery("select rowid, name from person1"), - rs2 = connAux.executeQuery("select rowid, name from person2"); - assertEquals(101, rs1.getRowCount()); - assertEquals(101, rs2.getRowCount()); - i = -1; - while (++i <= 100) - { - rs1.next(); - rs2.next(); - assertEquals("Name" + i, new String(rs1.getChars(2))); - assertEquals("Name" + i, new String(rs2.getChars("name"))); - } - rs1.close(); - rs2.close(); - - // juliana@214_5 juliana@214_6: the strings must be trimmed. - connAux.executeUpdate("drop table person2"); - connAux.execute("create table person2 (name char(1) primary key)"); - connAux.executeUpdate("insert into person2 values('')"); - connAux.executeUpdate("insert into person2 values('\\'')"); - - try // 'A is to be stored as ' since the field size is 1. - { - connAux.executeUpdate("insert into person2 values('\\'A')"); - fail("8"); - } - catch (PrimaryKeyViolationException exception) {} - - // Tests if the data was inserted correctly. - assertTrue((rs2 = connAux.executeQuery("select * from person2")).next()); - assertEquals("", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - assertTrue((rs2 = connAux.executeQuery("select * from person2 where name like '\\''")).next()); - assertEquals("'", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - - connAux.executeUpdate("drop table person2"); - connAux.execute("create table person2 (name char(2) primary key)"); - connAux.executeUpdate("insert into person2 values('')"); - connAux.executeUpdate("insert into person2 values('\\'')"); - connAux.executeUpdate("insert into person2 values('\\'A')"); - - try // 'AA is to be stored as 'A since the field size is 2. - { - connAux.executeUpdate("insert into person2 values('\\'AA')"); - fail("9"); - } - catch (PrimaryKeyViolationException exception) {} - - // Tests if the data was inserted correctly. - assertTrue((rs2 = connAux.executeQuery("select * from person2")).next()); - assertEquals("", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'A", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - assertTrue((rs2 = connAux.executeQuery("select * from person2 where name like '\\'%'")).next()); - assertEquals("'", rs2.getString("name")); - assertTrue(rs2.next()); - assertEquals("'A", rs2.getString("name")); - assertFalse(rs2.next()); - rs2.close(); - - // The tables are opened by its current connections. - assertTrue(conn.isOpen("PERSON1")); - assertTrue(connAux.isOpen("PERSON2")); - - // The tables are opened by the other connection. - assertFalse(connAux.isOpen("PERSON1")); - assertFalse(conn.isOpen("PERSON2")); - - conn.closeAll(); - connAux.closeAll(); - - conn = LitebaseConnection.getInstance("Test", "path = " + tempPath); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - - // The tables are still closed. - assertFalse(conn.isOpen("PERSON1")); - assertFalse(connAux.isOpen("PERSON2")); - assertFalse(connAux.isOpen("PERSON1")); - assertFalse(conn.isOpen("PERSON2")); - - conn.closeAll(); - connAux.closeAll(); - - // Not all the tables have the same cryptography mode. - try - { - LitebaseConnection.encryptTables("Test", tempPath, 1); - fail("10"); - } - catch (DriverException exception) {} - try - { - LitebaseConnection.decryptTables("Test", tempPath, 1); - fail("11"); - } - catch (DriverException exception) {} - - // Drops the table with cryptography. - (connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath)).executeUpdate("drop table person2"); - connAux.closeAll(); - - // Encrypts the table and tests it. - LitebaseConnection.encryptTables("Test", tempPath, 1); - conn = LitebaseConnection.getInstance("Test", tempPath); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - try // The table can't be opened in a non-encrypted connection. - { - conn.executeQuery("select * from person1"); - fail("12"); - } - catch (DriverException exception) {} - conn.closeAll(); - connAux.executeQuery("select * from person1").close(); - connAux.closeAll(); - - // Decrypts the table and tests it. - LitebaseConnection.decryptTables("Test", tempPath, 1); - conn = LitebaseConnection.getInstance("Test", tempPath); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - try // The table can't be opened in an encrypted connection. - { - connAux.executeQuery("select * from person1"); - fail("12"); - } - catch (DriverException exception) {} - connAux.closeAll(); - connAux = LitebaseConnection.getInstance("Test", "crypto; path = " + tempPath); - conn.executeQuery("select * from person1").close(); - conn.closeAll(); - } - -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestDate_DateTime.java b/LitebaseSDK/src/java/samples/sys/testcases/TestDate_DateTime.java deleted file mode 100644 index 4be9f51881..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestDate_DateTime.java +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * This test verifies if the DATE and DATETIME Litebase data fields are working properly. - */ -public class TestDate_DateTime extends TestCase -{ - LitebaseConnection driver; - - /** - * The main method of the test. - */ - public void testRun() - { - driver = AllTests.getInstance("Test"); - - // Inserts the items into a new table and executes the selects. - createTable(); - output("Do selects 0"); - doSelects(); - createTablePreparedStatement(); - output("Do selects 1"); - doSelects(); - - // Tests with prepared Statemet. - testPreparedStatement(); - testPreparedStatement2(); - createTablePrimarykeyDateTime(); - testSetDateTime(); - - // Tests like without indices. - driver.executeUpdate("alter table person drop primary key"); - testLikeStartsWithEqualsAnythingDateDateTime(); - driver.execute("create index idx on person(years)"); - driver.execute("create index idx on person(birth)"); - testLikeStartsWithEqualsAnythingDateDateTime(); - driver.closeAll(); - } - - /** - * Executes some selects in the table previously created. - */ - private void doSelects() - { - // Where clause with equalities. - ResultSet rs = driver.executeQuery("Select name, age,birth , years from person where birth = '2001/06/06'"); - assertNotNull(rs); - assertEquals(0, rs.getRowCount()); - rs.close(); - assertNotNull(rs = driver.executeQuery("Select name, age,birth , years from person where years = '2008/06/06 13:45'")); - assertEquals(1, rs.getRowCount()); - rs.close(); - - // Where clause with inequalities. - assertNotNull(rs = driver.executeQuery("Select name, age,birth , years from person where birth <= '2005-7/8'")); - assertEquals(2, rs.getRowCount()); - rs.close(); - assertNotNull(rs = driver.executeQuery("Select name, age,birth , years from person where years < '2007/06/06 0.0'")); - assertEquals(3, rs.getRowCount()); - rs.close(); - assertNotNull(rs = driver - .executeQuery("Select name, age,birth , years from person where birth > '2003/01/02' and years < '2030/06/06 1.59:40'")); - assertEquals(4, rs.getRowCount()); - rs.close(); - - // Aggregation and group by. - assertNotNull(rs = driver.executeQuery("Select years as aName, sum(age) as abirth from person group by years")); - assertTrue(rs.next()); - assertEquals("28.0", rs.getString("aBirth")); - assertTrue(rs.next()); - assertEquals("12.0", rs.getString("aBirth")); - assertTrue(rs.next()); - assertEquals("20.0", rs.getString("aBirth")); - assertEquals(3, rs.getRowCount()); - rs.close(); - - // Gets all the years of the table. - assertNotNull(rs = driver.executeQuery("Select year(birth) as years from person")); - assertTrue(rs.next()); - assertEquals("2005", rs.getString(1)); - assertTrue(rs.next()); - assertEquals("2005", rs.getString(1)); - assertTrue(rs.next()); - assertEquals("2005", rs.getString(1)); - assertTrue(rs.next()); - assertEquals("2005", rs.getString(1)); - assertFalse(rs.next()); - rs.close(); - - // Invalid date and datetime. - try - { - driver.executeQuery("Select * from person where birth = ''"); - fail("1"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("Select * from person where years = ''"); - fail("2"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("Select * from person where birth = ' '"); - fail("3"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("Select * from person where years = ' '"); - fail("4"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("Select * from person where years = '3000/12/31'"); - fail("5"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("Select * from person where years = '2010/02/29'"); - fail("6"); - } - catch (SQLParseException exception) {} - } - - /** - * Creates a table and populates it with normal selects. - */ - private void createTable() - { - output("Create simple table and insert."); - - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - driver.execute("create table person(name char(16) , age int, birth Date, years DateTime)"); - driver.execute("create index idx on person(years)"); - driver.executeUpdate("insert into person values ('renato novais',12, ' 2005/9-12 ', ' 2006/08-21 12:08:01:234 ')"); - driver.executeUpdate("insert into person values ('indira gomes',13, '2005/7/8 ', '2006/08-21 0:08')"); - driver.executeUpdate("insert into person values ('Lucas Novais',20, '2005/7/8', ' 2008/06/06 13:45 ')"); - driver.executeUpdate("insert into person values ('Zenes Lima',15, '2005/9/12 ', '2006/08-21 0:08')"); - - // Invalid date and datetime, - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, '', '1979/06-26 2:00')"); - fail("7"); - } - catch (SQLParseException exception) - { - exception.printStackTrace(); - } - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, '1979/06/26', '')"); - fail("8"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, ' ', '1979/06-26 2:00')"); - fail("9"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, '1979/06/26', ' ')"); - fail("10"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, '1979/02/29', '1979/06-26 2:00')"); - fail("11"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, '3000/26/06', '1979/06-26 2:00')"); - fail("12"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into person values ('Juliana Imperial', 29, '1979/26/06', '1979/06-26 2:00:00.000.0')"); - fail("13"); - } - catch (SQLParseException exception) {} - } - - /** - * Does a simple select and uses it to get the number of rows of a table. - * - * @param tableName The table whose rows are to be counted. - * @return The table number of rows. - */ - private int getRowCount(String tableName) // rnovais@_570_56 - { - ResultSet rs = driver.executeQuery("Select * from " + tableName); - int ret = rs.getRowCount(); - rs.close(); - return ret; - } - - /** - * Creates and populates a table using prepared statement. - */ - private void createTablePreparedStatement() - { - output("Create simple table and insert with prepared statement."); - - driver.prepareStatement("drop table person").executeUpdate(); - driver.prepareStatement("create table person(name char(16) , age int, birth Date, years DateTime)").executeUpdate(); - - PreparedStatement ps = driver.prepareStatement("insert into person values(?,?,?,?)"); - - // Inserts the first. - ps.setString(0, "renato novais"); - ps.setInt(1, 12); - ps.setString(2, " 2005/9-12 "); - ps.setString(3, " 2006/08-21 12:08:01:0 "); - ps.executeUpdate(); - - // Inserts the second. - ps.setString(0, "indira gomes"); - ps.setInt(1, 13); - ps.setString(2, "2005/7/8 "); - ps.setString(3, "2006/08-21 0:08"); - ps.executeUpdate(); - - // Inserts the third. - ps.setString(0, "Lucas Novais"); - ps.setInt(1, 20); - ps.setString(2, "05/7/8 "); - ps.setString(3, " 2008/06/06 13:45"); - ps.executeUpdate(); - - // Inserts the fourth. - ps.setString(0, "Zenes Lima"); - ps.setInt(1, 15); - ps.setString(2, " 2005/9/12"); - ps.setString(3, "2006/08-21 0:08"); - ps.executeUpdate(); - - assertEquals(4, getRowCount("person")); - - // Invalid date. - ps.setString(0, "Juliana Imperial"); - ps.setInt(1, 29); - ps.setString(2, ""); - ps.setString(3, " 1979/06-26 00:02:00:0 "); - try - { - ps.executeUpdate(); - fail("14"); - } - catch (SQLParseException exception) {} - - // Invalid datetime. - ps.setString(0, "Juliana Imperial"); - ps.setInt(1, 29); - ps.setString(2, " 1979/06/26"); - ps.setString(3, ""); - try - { - ps.executeUpdate(); - fail("15"); - } - catch (SQLParseException exception) {} - - // Invalid date. - ps.setString(0, "Juliana Imperial"); - ps.setInt(1, 29); - ps.setString(2, " "); - ps.setString(3, " 1979/06-26 00:02:00:0 "); - try - { - ps.executeUpdate(); - fail("16"); - } - catch (SQLParseException exception) {} - - // Invalid datetime. - ps.setString(0, "Juliana Imperial"); - ps.setInt(1,29); - ps.setString(2, " 1979/06/26"); - ps.setString(3, " "); - try - { - ps.executeUpdate(); - fail("17"); - } - catch (SQLParseException exception) {} - - // Inserts and deletes a valid value. - try - { - ps.setString(0, "Juliana Imperial"); - ps.setInt(1, 29); - ps.setDate(2, new Date("26/06/1979", Settings.DATE_DMY)); - ps.setDateTime(3, new Date("09/02/2009", Settings.DATE_DMY)); - ps.executeUpdate(); - } - catch (SQLParseException exception) - { - fail("18"); - } - catch (InvalidDateException exception) - { - fail(exception.getMessage()); - } - driver.executeUpdate("delete from person where years = '2009/02/09'"); - } - - /** - * Does some tests using update and delete prepared statements. - */ - private void testPreparedStatement() // rnovais@_570_56 - { - output("Test Prepared Statement."); - if (driver.exists("person2")) - driver.prepareStatement(" drop table person2 ").executeUpdate(); - - driver.prepareStatement(" create table person2(name char(16) , age int, birth Date, years DateTime) ").executeUpdate(); - driver.prepareStatement(" create index idx on person2(years) ").executeUpdate(); - driver.prepareStatement(" create index idx on person2(birth) ").executeUpdate(); - - PreparedStatement ps = driver.prepareStatement(" insert into person2 values(?,?,?,?) "); - - // Inserts the first. - ps.setString(0, "renato novais"); - ps.setInt(1, 12); - ps.setString(2, " 2005/9-12 "); - ps.setString(3, " 2006/08-21 12:08:01:0 "); - ps.executeUpdate(); - - // Inserts the second. - ps.setString(0, "indira gomes"); - ps.setInt(1, 13); - ps.setString(2, "2005/7/8 "); - ps.setString(3, "2006/08-21 0:08"); - ps.executeUpdate(); - - // Inserts the third. - ps.setString(0, "Lucas Novais"); - ps.setInt(1, 20); - ps.setString(2, "05/7/8 "); - ps.setString(3, " 2008/06/06 13:45"); - ps.executeUpdate(); - - // Inserts the fourth. - ps.setString(0, "Zenes Lima"); - ps.setInt(1, 15); - ps.setString(2, " 2005/9/12"); - ps.setString(3, "2006/08-21 0:08"); - ps.executeUpdate(); - assertEquals(4, getRowCount("person2")); - - // juliana@220_6: setDate() and setDateTime() must accept null values. - ps.setDate(2, null); - ps.setDateTime(3, (Date)null); - ps.setDateTime(3, (Time)null); - - try - { - // Updates // rnovais@570_56 - (ps = driver.prepareStatement("update person2 set name = ? where birth = ?")).setString(0, "danilo"); - ps.setDate(1, new Date("8/7/2005", Settings.DATE_DMY)); - assertEquals(2, ps.executeUpdate()); - assertEquals(4, getRowCount("person2")); - - (ps = driver.prepareStatement("update person2 set name = ? where years = ?")).setString(0, "carol"); - ps.setDateTime(1, new Time("20060821T12:08:01")); - assertEquals(1, ps.executeUpdate()); - assertEquals(4, getRowCount("person2")); - - // Deletes // rnovais@570_56 - (ps = driver.prepareStatement("delete from person2 where birth = ?")).setDate(0, new Date("12/09/2005", Settings.DATE_DMY)); - assertEquals(2, ps.executeUpdate()); - assertEquals(2, getRowCount("person2")); - - (ps = driver.prepareStatement("delete from person2 where years = ?")).setDateTime(0, new Time("20060821T00:08:00")); - assertEquals(1, ps.executeUpdate()); - assertEquals(1, getRowCount("person2")); - } - catch (InvalidNumberException exception) - { - fail(exception.getMessage()); - } - catch (InvalidDateException exception) - { - fail(exception.getMessage()); - } - } - - /** - * Does some tests using select prepared statements with indices and functions. - */ - private void testPreparedStatement2() // rnovais@570_108 - { - output("Test Prepared Statement 2."); - - driver.prepareStatement(" drop index * on person2 ").executeUpdate(); - driver.prepareStatement(" drop table person2 ").executeUpdate(); - - driver.prepareStatement("create table person2(name char(16) , age int, birth Date, years DateTime)").executeUpdate(); - driver.prepareStatement("create index idx on person2(years)").executeUpdate(); - PreparedStatement ps = driver.prepareStatement("insert into person2 values(?,?,?,?)"); - - // Inserts the first. - ps.setString(0, "renato novais"); - ps.setInt(1, 12); - ps.setString(2, " 2005/9-12 "); - ps.setString(3, " 2006/08-21 12:08:01:0 "); - ps.executeUpdate(); - driver.prepareStatement("insert into person2 values ('jo�o pedro',13, '2006/7/8 ', '2006/08-21 0:08')").executeUpdate(); - driver.prepareStatement("insert into person2 values ('danilo novais',20, '2008/4/6', ' 2008/06/06 13:45 ')").executeUpdate(); - driver.prepareStatement("insert into person2 values ('carol nOVAIS',15, '2005/9/12 ', '2005/01-4 1:50')").executeUpdate(); - - try - { - (ps = driver.prepareStatement("select * from person2 where birth = ?")).setDate(0, new Date("8/7/2006", Settings.DATE_DMY)); - ResultSet rs = ps.executeQuery(); - assertTrue(rs.next()); - assertEquals("jo�o pedro", rs.getString(1)); - rs.close(); - - (ps = driver.prepareStatement("select name from person2 where month(birth) = ? and day(birth) = ? and minute(years) = 50 and millis(years) != ?")).setShort(0, (short)9); - ps.setShort(1, (short)12); - ps.setShort(2, (short)1); - assertTrue((rs = ps.executeQuery()).next()); - assertEquals("carol nOVAIS", rs.getString(1)); - rs.close(); - - (ps = driver.prepareStatement("select name from person2 where month(birth) != ? and (day(birth) = ? or minute(years) = ?)")).setShort(0, (short)7); - ps.setShort(1, (short)6); - ps.setShort(2, (short)8); - assertEquals(2, (rs = ps.executeQuery()).getRowCount()); - assertTrue(rs.next()); - assertEquals("renato novais", rs.getString(1)); - assertTrue(rs.next()); - assertEquals("danilo novais", rs.getString(1)); - rs.close(); - - (ps = driver.prepareStatement("select name, birth, years from person2 where (day(birth) = ? and birth != ?) or minute(years) = ?")).setShort(0, (short)6); - ps.setDate(1, new Date("8/7/2006", Settings.DATE_DMY)); - ps.setShort(2, (short)8); - assertEquals(3, (rs = ps.executeQuery()).getRowCount()); - assertTrue(rs.next()); - assertEquals("renato novais", rs.getString(1)); - - int settings = Settings.dateFormat; - boolean is24Hour = Settings.is24Hour; - - Settings.dateFormat = Settings.DATE_DMY; - Settings.is24Hour = true; - - assertEquals("12/09/2005", rs.getString(2)); - assertEquals("21/08/2006 12:08:01:000", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("jo�o pedro", rs.getString(1)); - assertEquals("08/07/2006", rs.getString(2)); - assertEquals("21/08/2006 00:08:00:000", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("danilo novais", rs.getString(1)); - assertEquals("06/04/2008", rs.getString(2)); - assertEquals("06/06/2008 13:45:00:000", rs.getString(3)); - - Settings.dateFormat = (byte)settings; - Settings.is24Hour = is24Hour; - rs.close(); - } - catch (InvalidDateException exception) - { - fail(exception.getMessage()); - } - } - - /** - * Tests duplicates insertions with primary key. - */ - private void createTablePrimarykeyDateTime() - { - driver.executeUpdate("drop table person"); - - // Primary key on a date field. - driver.execute("create table person(name char(16), age int, birth date primary key, years DateTime)"); - - // Duplicated date. - assertEquals(1, driver.executeUpdate("insert into person values ('renato novais',12,'2005/9-12', '2006/08-21 12:08')")); - try - { - driver.executeUpdate("insert into person values ('indira gomes',13,'2005/9-12', '2007/09/20')"); - fail("1. PrimaryKeyViolationException not thrown"); - } - catch (PrimaryKeyViolationException exception) {} - - // Primary key on a datetime field. - driver.executeUpdate("alter table person drop primary key"); - driver.executeUpdate("alter table person add primary key (years)"); - driver.executeUpdate("delete from person"); - - // Duplicated datetime. - assertEquals(1, driver.executeUpdate("insert into person values ('renato novais',12,'2005/9-12', '2006/08-21 12:08')")); - try - { - driver.executeUpdate("insert into person values ('indira gomes',13,'2007/06/15', '2006/08-21 12:08')"); - fail("2. PrimaryKeyViolationException not thrown"); - } - catch (PrimaryKeyViolationException exception) {} - - } - - /** - * Tests that setDateTime inserts the correct value of a DATETIME using a time object. - */ - private void testSetDateTime() - { - if (driver.exists("time")) - driver.executeUpdate("drop table time"); - driver.execute("CREATE TABLE time (campo DATETIME primary key, data date default '1979/06/26')"); - PreparedStatement pstmt = driver.prepareStatement("INSERT INTO time (campo) VALUES(?)"); - Time now = new Time(); - pstmt.setDateTime(0, now); - pstmt.executeUpdate(); - ResultSet rs = driver.executeQuery("SELECT * FROM time"); - rs.next(); - assertEquals(now.toIso8601(), rs.getDateTime(1).toIso8601()); - try - { - assertEquals(new Date(19790626), rs.getDate(2)); - } - catch (InvalidDateException exception) {} - rs.close(); - } - - /** - * Tests DATE and DATETIME with like using the pattern mathes EQUALS, STARTS_WITH, and ANYTHING. - */ - private void testLikeStartsWithEqualsAnythingDateDateTime() - { - ResultSet resultSet = driver.executeQuery("select * from person where birth like '2005/09/12'"); - assertEquals(1, resultSet.getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '20%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '200%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005/%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005/0%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005/09%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005/09/%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005/09/1%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where birth like '2005/09/12%'")).getRowCount()); - resultSet.close(); - - assertEquals(0, (resultSet = driver.executeQuery("select * from person where birth like '2005/09/12 %'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:00:000%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '20%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '200%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/0%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/2%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 %'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 1%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:0%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:0%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:00%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:00:%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:00:0%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:00:00%'")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driver.executeQuery("select * from person where years like '2006/08/21 12:08:00:000%'")).getRowCount()); - resultSet.close(); - } - -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestDeleteAndMetaData.java b/LitebaseSDK/src/java/samples/sys/testcases/TestDeleteAndMetaData.java deleted file mode 100644 index a2815b822e..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestDeleteAndMetaData.java +++ /dev/null @@ -1,1275 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * This tests if a table was successfully deleted, if the insert of a Long is working and the meta data class. - */ -public class TestDeleteAndMetaData extends TestCase -{ - LitebaseConnection driver; - ResultSet rs; - - /** - * Tests the result set meta data. - */ - private void testMetaData() - { - // In table's order. - rs = driver.executeQuery("select time as Tempo, name from tabsync order by rowid"); - ResultSetMetaData meta = rs.getResultSetMetaData(); - - assertEquals(2, meta.getColumnCount()); - assertEquals(20, meta.getColumnDisplaySize(1)); - assertEquals(5, meta.getColumnDisplaySize(2)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("1"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(3); - fail("2"); - } - catch (IllegalArgumentException exception) {} - - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(1)); - assertEquals(ResultSetMetaData.CHAR_NOCASE_TYPE, meta.getColumnType(2)); - - // Invalid Column index. - try - { - meta.getColumnType(0); - fail("3"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnType(3); - fail("4"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("long", meta.getColumnTypeName(1)); - assertEquals("chars", meta.getColumnTypeName(2)); - - // Invalid Column index. - try - { - meta.getColumnTypeName(0); - fail("5"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnTypeName(3); - fail("6"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("tempo", meta.getColumnLabel(1)); - assertEquals("name", meta.getColumnLabel(2)); - - // Invalid Column index. - try - { - meta.getColumnLabel(0); - fail("7"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnLabel(3); - fail("8"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("tabsync", meta.getColumnTableName("time")); - assertEquals("tabsync", meta.getColumnTableName("Tempo")); - assertEquals("tabsync", meta.getColumnTableName("name")); - - // Invalid column name. - try - { - meta.getColumnTableName(null); - fail("9"); - } - catch (NullPointerException exception) {} - try - { - meta.getColumnTableName("barbara"); - fail("10"); - } - catch (DriverException exception) {} - - assertEquals("tabsync", meta.getColumnTableName(1)); - assertEquals("tabsync", meta.getColumnTableName(2)); - - // Invalid Column index. - try - { - meta.getColumnTableName(0); - fail("11"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnTableName(3); - fail("12"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.isNotNull(1)); - assertTrue(meta.isNotNull(2)); - - // Invalid Column index. - try - { - meta.isNotNull(0); - fail("13"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.isNotNull(3); - fail("14"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.isNotNull("Tempo")); - assertFalse(meta.isNotNull("time")); - assertTrue(meta.isNotNull("name")); - - // Invalid Column name. - try - { - meta.isNotNull(null); - fail("15"); - } - catch (NullPointerException exception) {} - try - { - meta.isNotNull("juliana"); - fail("16"); - } - catch (DriverException exception) {} - - assertTrue(meta.hasDefaultValue(1)); - assertEquals("1", meta.getDefaultValue(1)); - assertFalse(meta.hasDefaultValue(2)); - assertNull(meta.getDefaultValue(2)); - - // Invalid Column index. - try - { - meta.hasDefaultValue(0); - fail("17"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.hasDefaultValue(3); - fail("18"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(0); - fail("19"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(3); - fail("20"); - } - catch (IllegalArgumentException exception) {} - - assertTrue(meta.hasDefaultValue("Tempo")); - assertEquals("1", meta.getDefaultValue("Tempo")); - assertTrue(meta.hasDefaultValue("time")); - assertEquals("1", meta.getDefaultValue("time")); - assertFalse(meta.hasDefaultValue("name")); - assertNull(meta.getDefaultValue("name")); - - // Invalid Column name. - try - { - meta.hasDefaultValue(null); - fail("21"); - } - catch (NullPointerException exception) {} - try - { - meta.hasDefaultValue("juliana"); - fail("22"); - } - catch (DriverException exception) {} - try - { - meta.getDefaultValue(null); - fail("23"); - } - catch (NullPointerException exception) {} - try - { - meta.getDefaultValue("juliana"); - fail("24"); - } - catch (DriverException exception) {} - - assertEquals(1, meta.getPKColumnIndices("tabSync")[0]); - assertEquals("time", meta.getPKColumnNames("tabSync")[0]); - - // Table is not in the select. - try - { - meta.getPKColumnIndices("tabsync2"); - fail("25"); - } - catch (DriverException exception) {} - try - { - meta.getPKColumnNames("tabsync2"); - fail("26"); - } - catch (DriverException exception) {} - - try - { - meta.getPKColumnIndices(null); - fail("27"); - } - catch (NullPointerException exception) {} - catch (DriverException exception) {} - try - { - meta.getPKColumnNames(null); - fail("28"); - } - catch (NullPointerException exception) {} - catch (DriverException exception) {} - - rs.close(); - - // Out of table's order. - assertEquals(2, (meta = (rs = driver.executeQuery("select name, time as Tempo from tabsync")).getResultSetMetaData()).getColumnCount()); - assertEquals(5, meta.getColumnDisplaySize(1)); - assertEquals(20, meta.getColumnDisplaySize(2)); - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(2)); - assertEquals(ResultSetMetaData.CHAR_NOCASE_TYPE, meta.getColumnType(1)); - assertEquals("chars", meta.getColumnTypeName(1)); - assertEquals("long", meta.getColumnTypeName(2)); - assertEquals("name", meta.getColumnLabel(1)); - assertEquals("tempo", meta.getColumnLabel(2)); - assertEquals("tabsync", meta.getColumnTableName("time")); - assertEquals("tabsync", meta.getColumnTableName("Tempo")); - assertEquals("tabsync", meta.getColumnTableName("name")); - assertEquals("tabsync", meta.getColumnTableName(1)); - assertEquals("tabsync", meta.getColumnTableName(2)); - assertTrue(meta.isNotNull(1)); - assertFalse(meta.isNotNull(2)); - assertFalse(meta.isNotNull("Tempo")); - assertFalse(meta.isNotNull("time")); - assertTrue(meta.isNotNull("name")); - assertFalse(meta.hasDefaultValue(1)); - assertTrue(meta.hasDefaultValue(2)); - assertEquals("1", meta.getDefaultValue(2)); - assertTrue(meta.hasDefaultValue("Tempo")); - assertEquals("1", meta.getDefaultValue("Tempo")); - assertTrue(meta.hasDefaultValue("time")); - assertEquals("1", meta.getDefaultValue("time")); - assertFalse(meta.hasDefaultValue("name")); - assertNull(meta.getDefaultValue("name")); - assertEquals(1, meta.getPKColumnIndices("tabsync")[0]); - assertEquals("time", meta.getPKColumnNames("tabsync")[0]); - rs.close(); - - // Simple select: select * from tablename. - assertEquals(2, (meta = (rs = driver.executeQuery("select * from tabsync")).getResultSetMetaData()).getColumnCount()); - assertEquals(5, meta.getColumnDisplaySize(2)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("29"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(3); - fail("30"); - } - catch (IllegalArgumentException exception) {} - - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(1)); - assertEquals(ResultSetMetaData.CHAR_NOCASE_TYPE, meta.getColumnType(2)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("31"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(3); - fail("32"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("long", meta.getColumnTypeName(1)); - assertEquals("chars", meta.getColumnTypeName(2)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("33"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(3); - fail("34"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("time", meta.getColumnLabel(1)); - assertEquals("name", meta.getColumnLabel(2)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("35"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(3); - fail("36"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("tabsync", meta.getColumnTableName("TIME")); - assertEquals("tabsync", meta.getColumnTableName("namE")); - - // Ivalid column name. - try - { - meta.getColumnTableName("Tempo"); - fail("37"); - } - catch (DriverException exception) {} - try - { - meta.getColumnTableName(null); - fail("38"); - } - catch (NullPointerException exception) {} - - assertEquals("tabsync", meta.getColumnTableName(1)); - assertEquals("tabsync", meta.getColumnTableName(2)); - - // Invalid column index. - try - { - meta.getColumnTableName(0); - fail("39"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnTableName(3); - fail("40"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.isNotNull(1)); - assertTrue(meta.isNotNull(2)); - - // Invalid Column index. - try - { - meta.isNotNull(0); - fail("41"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.isNotNull(3); - fail("42"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.isNotNull("time")); - assertTrue(meta.isNotNull("name")); - - // Invalid Column index. - try - { - meta.isNotNull(null); - fail("43"); - } - catch (NullPointerException exception) {} - try - { - meta.isNotNull("tempo"); - fail("44"); - } - catch (DriverException exception) {} - - assertTrue(meta.hasDefaultValue(1)); - assertEquals("1", meta.getDefaultValue(1)); - assertFalse(meta.hasDefaultValue(2)); - assertNull(meta.getDefaultValue(2)); - - // Invalid Column index. - try - { - meta.hasDefaultValue(0); - fail("45"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.hasDefaultValue(3); - fail("46"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(0); - fail("47"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(3); - fail("48"); - } - catch (IllegalArgumentException exception) {} - - assertEquals(true, meta.hasDefaultValue("time")); - assertEquals("1", meta.getDefaultValue("time")); - assertEquals(false, meta.hasDefaultValue("name")); - assertNull(meta.getDefaultValue("name")); - - // Invalid Column index. - try - { - meta.hasDefaultValue(null); - fail("49"); - } - catch (NullPointerException exception) {} - try - { - meta.hasDefaultValue("juliana"); - fail("50"); - } - catch (DriverException exception) {} - try - { - meta.getDefaultValue(null); - fail("51"); - } - catch (NullPointerException exception) {} - try - { - meta.getDefaultValue("juliana"); - fail("52"); - } - catch (DriverException exception) {} - - assertEquals(1, meta.getPKColumnIndices("tabsync")[0]); - assertEquals("time", meta.getPKColumnNames("tabsync")[0]); - - rs.close(); - - // Tests what happens if the result set is closed in a simple query. - try - { - meta.getColumnCount(); - fail("53"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnDisplaySize(1); - fail("54"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnType(1); - fail("55"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnLabel(1); - fail("56"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnTableName(1); - fail("57"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnTableName("TIME"); - fail("58"); - } - catch (IllegalStateException exception) {} - try - { - meta.isNotNull(1); - fail("59"); - } - catch (IllegalStateException exception) {} - try - { - meta.isNotNull("TIME"); - fail("60"); - } - catch (IllegalStateException exception) {} - try - { - meta.hasDefaultValue(1); - fail("61"); - } - catch (IllegalStateException exception) {} - try - { - meta.hasDefaultValue("TIME"); - fail("62"); - } - catch (IllegalStateException exception) {} - try - { - meta.getDefaultValue(1); - fail("63"); - } - catch (IllegalStateException exception) {} - try - { - meta.getDefaultValue("TIME"); - fail("64"); - } - catch (IllegalStateException exception) {} - try - { - meta.getPKColumnIndices("tabsync"); - fail("65"); - } - catch (IllegalStateException exception) {} - try - { - meta.getPKColumnNames("tabsync"); - fail("66"); - } - catch (IllegalStateException exception) {} - - // Simple select: select rowid, time, name from tablename. - assertEquals(3, (meta = (rs = driver.executeQuery("select rowid, time, name from tabsync")).getResultSetMetaData()).getColumnCount()); - assertEquals(11, meta.getColumnDisplaySize(1)); - assertEquals(20, meta.getColumnDisplaySize(2)); - assertEquals(5, meta.getColumnDisplaySize(3)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("67"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(4); - fail("68"); - } - catch (IllegalArgumentException exception) {} - - assertEquals(ResultSetMetaData.INT_TYPE, meta.getColumnType(1)); - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(2)); - assertEquals(ResultSetMetaData.CHAR_NOCASE_TYPE, meta.getColumnType(3)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("69"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(4); - fail("70"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("int", meta.getColumnTypeName(1)); - assertEquals("long", meta.getColumnTypeName(2)); - assertEquals("chars", meta.getColumnTypeName(3)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("71"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(4); - fail("72"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("rowid", meta.getColumnLabel(1)); - assertEquals("time", meta.getColumnLabel(2)); - assertEquals("name", meta.getColumnLabel(3)); - - // Invalid Column index. - try - { - meta.getColumnDisplaySize(0); - fail("73"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnDisplaySize(4); - fail("74"); - } - catch (IllegalArgumentException exception) {} - - assertEquals("tabsync", meta.getColumnTableName("rowid")); - assertEquals("tabsync", meta.getColumnTableName("TIME")); - assertEquals("tabsync", meta.getColumnTableName("namE")); - - // Ivalid column name. - try - { - meta.getColumnTableName("Tempo"); - fail("75"); - } - catch (DriverException exception) {} - try - { - meta.getColumnTableName(null); - fail("76"); - } - catch (NullPointerException exception) {} - - assertEquals("tabsync", meta.getColumnTableName(1)); - assertEquals("tabsync", meta.getColumnTableName(2)); - assertEquals("tabsync", meta.getColumnTableName(3)); - - // Invalid column index. - try - { - meta.getColumnTableName(0); - fail("77"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnTableName(4); - fail("78"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.isNotNull(1)); - assertFalse(meta.isNotNull(2)); - assertTrue(meta.isNotNull(3)); - - // Invalid Column index. - try - { - meta.isNotNull(0); - fail("79"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.isNotNull(4); - fail("80"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.isNotNull("rowid")); - assertFalse(meta.isNotNull("time")); - assertTrue(meta.isNotNull("name")); - - // Invalid Column index. - try - { - meta.isNotNull(null); - fail("81"); - } - catch (NullPointerException exception) {} - try - { - meta.isNotNull("Tempo"); - fail("82"); - } - catch (DriverException exception) {} - - assertFalse(meta.hasDefaultValue(1)); - assertNull(meta.getDefaultValue(1)); - assertTrue(meta.hasDefaultValue(2)); - assertEquals("1", meta.getDefaultValue(2)); - assertFalse(meta.hasDefaultValue(3)); - assertNull(meta.getDefaultValue(3)); - - // Invalid Column index. - try - { - meta.hasDefaultValue(0); - fail("83"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.hasDefaultValue(4); - fail("84"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(0); - fail("85"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(4); - fail("86"); - } - catch (IllegalArgumentException exception) {} - - assertFalse(meta.hasDefaultValue("rowid")); - assertNull(meta.getDefaultValue("rowid")); - assertTrue(meta.hasDefaultValue("time")); - assertEquals("1", meta.getDefaultValue("time")); - assertFalse(meta.hasDefaultValue("name")); - assertNull(meta.getDefaultValue("name")); - - // Invalid Column index. - try - { - meta.hasDefaultValue(null); - fail("87"); - } - catch (NullPointerException exception) {} - try - { - meta.hasDefaultValue("juliana"); - fail("88"); - } - catch (DriverException exception) {} - try - { - meta.getDefaultValue(null); - fail("89"); - } - catch (NullPointerException exception) {} - try - { - meta.getDefaultValue("juliana"); - fail("90"); - } - catch (DriverException exception) {} - - assertEquals(1, meta.getPKColumnIndices("tabsync")[0]); - assertEquals("time", meta.getPKColumnNames("tabsync")[0]); - - rs.close(); - - // Tests what happens if the result set is closed in a simple query. - try - { - meta.getColumnCount(); - fail("91"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnDisplaySize(1); - fail("92"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnType(1); - fail("93"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnLabel(1); - fail("94"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnTableName(1); - fail("95"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnTableName("TIME"); - fail("96"); - } - catch (IllegalStateException exception) {} - try - { - meta.isNotNull(1); - fail("97"); - } - catch (IllegalStateException exception) {} - try - { - meta.isNotNull("TIME"); - fail("98"); - } - catch (IllegalStateException exception) {} - try - { - meta.hasDefaultValue(1); - fail("99"); - } - catch (IllegalStateException exception) {} - try - { - meta.hasDefaultValue("TIME"); - fail("100"); - } - catch (IllegalStateException exception) {} - try - { - meta.getDefaultValue(1); - fail("101"); - } - catch (IllegalStateException exception) {} - try - { - meta.getDefaultValue("TIME"); - fail("102"); - } - catch (IllegalStateException exception) {} - try - { - meta.getPKColumnIndices("tabsync"); - fail("103"); - } - catch (IllegalStateException exception) {} - try - { - meta.getPKColumnNames("tabsync"); - fail("104"); - } - catch (IllegalStateException exception) {} - - // Join - assertEquals(4, (meta = (rs = driver.executeQuery("select * from tabsync, tabsync2")).getResultSetMetaData()).getColumnCount()); - assertEquals(5, meta.getColumnDisplaySize(2)); - assertEquals(5, meta.getColumnDisplaySize(4)); - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(1)); - assertEquals(ResultSetMetaData.CHAR_NOCASE_TYPE, meta.getColumnType(2)); - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(3)); - assertEquals(ResultSetMetaData.CHAR_NOCASE_TYPE, meta.getColumnType(4)); - assertEquals("long", meta.getColumnTypeName(1)); - assertEquals("chars", meta.getColumnTypeName(2)); - assertEquals("long", meta.getColumnTypeName(3)); - assertEquals("chars", meta.getColumnTypeName(4)); - assertEquals("time", meta.getColumnLabel(1)); - assertEquals("name", meta.getColumnLabel(2)); - assertEquals("life", meta.getColumnLabel(3)); - assertEquals("name", meta.getColumnLabel(4)); - assertEquals("tabsync", meta.getColumnTableName("TIME")); - try - { - meta.getColumnTableName("Tempo"); - fail("105"); - } - catch (DriverException exception) {} - assertEquals("tabsync", meta.getColumnTableName("namE")); - assertEquals("tabsync2", meta.getColumnTableName("life")); - assertEquals("tabsync", meta.getColumnTableName(1)); - assertEquals("tabsync", meta.getColumnTableName(2)); - assertEquals("tabsync2", meta.getColumnTableName(3)); - assertEquals("tabsync2", meta.getColumnTableName(4)); - try - { - meta.getColumnTableName(null); - fail("106"); - } - catch (NullPointerException exception) {} - try - { - meta.getColumnTableName(0); - fail("107"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getColumnTableName(5); - fail("108"); - } - catch (IllegalArgumentException exception) {} - assertFalse(meta.isNotNull(1)); - assertTrue(meta.isNotNull(2)); - assertFalse(meta.isNotNull(3)); - assertFalse(meta.isNotNull(4)); - try - { - meta.isNotNull(0); - fail("109"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.isNotNull(5); - fail("110"); - } - catch (IllegalArgumentException exception) {} - assertFalse(meta.isNotNull("time")); - assertTrue(meta.isNotNull("name")); - assertFalse(meta.isNotNull("life")); - try - { - meta.isNotNull(null); - fail("111"); - } - catch (NullPointerException exception) {} - try - { - meta.isNotNull("Tempo"); - fail("112"); - } - catch (DriverException exception) {} - assertTrue(meta.hasDefaultValue(1)); - assertEquals("1", meta.getDefaultValue(1)); - assertFalse(meta.hasDefaultValue(2)); - assertNull(meta.getDefaultValue(2)); - assertFalse(meta.hasDefaultValue(3)); - assertNull(meta.getDefaultValue(3)); - assertTrue(meta.hasDefaultValue(4)); - assertEquals("Juli", meta.getDefaultValue(4)); - try - { - meta.hasDefaultValue(0); - fail("113"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.hasDefaultValue(5); - fail("114"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(0); - fail("115"); - } - catch (IllegalArgumentException exception) {} - try - { - meta.getDefaultValue(5); - fail("116"); - } - catch (IllegalArgumentException exception) {} - assertTrue(meta.hasDefaultValue("time")); - assertEquals("1", meta.getDefaultValue("time")); - assertFalse(meta.hasDefaultValue("name")); - assertNull(meta.getDefaultValue("name")); - assertFalse(meta.hasDefaultValue("life")); - assertNull(meta.getDefaultValue("life")); - try - { - meta.hasDefaultValue(null); - fail("117"); - } - catch (NullPointerException exception) {} - try - { - meta.hasDefaultValue("Tempo"); - fail("118"); - } - catch (DriverException exception) {} - try - { - meta.getDefaultValue(null); - fail("119"); - } - catch (NullPointerException exception) {} - try - { - meta.getDefaultValue("Tempo"); - fail("120"); - } - catch (DriverException exception) {} - assertEquals(1, meta.getPKColumnIndices("tabsync")[0]); - assertEquals("time", meta.getPKColumnNames("tabsync")[0]); - assertEquals(2, meta.getPKColumnIndices("tabsync2")[0]); - assertEquals(1, meta.getPKColumnIndices("tabsync2")[1]); - assertEquals("name", meta.getPKColumnNames("tabsync2")[0]); - assertEquals("life", meta.getPKColumnNames("tabsync2")[1]); - rs.close(); - - // Aggregation and functions. - assertEquals(4, (meta = (rs = driver.executeQuery("select count(*) as c, upper(tabsync.name) as n1, lower(tabsync2.name) as n2, " - + "abs(tabsync.time) as abt from tabsync, tabsync2")).getResultSetMetaData()).getColumnCount()); - assertEquals(11, meta.getColumnDisplaySize(1)); - assertEquals(5, meta.getColumnDisplaySize(2)); - assertEquals(5, meta.getColumnDisplaySize(3)); - assertEquals(20, meta.getColumnDisplaySize(4)); - assertEquals(ResultSetMetaData.INT_TYPE, meta.getColumnType(1)); - assertEquals(ResultSetMetaData.CHAR_TYPE, meta.getColumnType(2)); - assertEquals(ResultSetMetaData.CHAR_TYPE, meta.getColumnType(3)); - assertEquals(ResultSetMetaData.LONG_TYPE, meta.getColumnType(4)); - assertEquals("int", meta.getColumnTypeName(1)); - assertEquals("chars", meta.getColumnTypeName(2)); - assertEquals("chars", meta.getColumnTypeName(3)); - assertEquals("long", meta.getColumnTypeName(4)); - assertEquals("c", meta.getColumnLabel(1)); - assertEquals("n1", meta.getColumnLabel(2)); - assertEquals("n2", meta.getColumnLabel(3)); - assertEquals("abt", meta.getColumnLabel(4)); - assertNull(meta.getColumnTableName(1)); - assertNull(meta.getColumnTableName("c")); - assertEquals("tabsync", meta.getColumnTableName(2)); - assertEquals("tabsync", meta.getColumnTableName("n1")); - assertEquals("tabsync2", meta.getColumnTableName(3)); - assertEquals("tabsync2", meta.getColumnTableName("n2")); - assertEquals("tabsync", meta.getColumnTableName(4)); - assertEquals("tabsync", meta.getColumnTableName("abt")); - try // Column does not have an underlining table. - { - meta.isNotNull(1); - fail("121"); - } - catch (DriverException exception) {} - assertTrue(meta.isNotNull(2)); - assertFalse(meta.isNotNull(3)); - assertFalse(meta.isNotNull(4)); - try // Column does not have an underlining table. - { - meta.isNotNull("c"); - fail("122"); - } - catch (DriverException exception) {} - assertTrue(meta.isNotNull("n1")); - assertFalse(meta.isNotNull("n2")); - assertFalse(meta.isNotNull("abt")); - try // Column does not have an underlining table. - { - meta.hasDefaultValue(1); - fail("123"); - } - catch (DriverException exception) {} - assertFalse(meta.hasDefaultValue(2)); - assertNull(meta.getDefaultValue(2)); - assertTrue(meta.hasDefaultValue(3)); - assertEquals("Juli", meta.getDefaultValue(3)); - assertTrue(meta.hasDefaultValue(4)); - assertEquals("1", meta.getDefaultValue(4)); - try // Column does not have an underlining table. - { - meta.hasDefaultValue("c"); - fail("124"); - } - catch (DriverException exception) {} - try // Column does not have an underlining table. - { - meta.getDefaultValue("c"); - fail("125"); - } - catch (DriverException exception) {} - assertFalse(meta.hasDefaultValue("n1")); - assertNull(meta.getDefaultValue("n1")); - assertTrue(meta.hasDefaultValue("n2")); - assertEquals("Juli", meta.getDefaultValue("n2")); - assertTrue(meta.hasDefaultValue("abt")); - assertEquals("1", meta.getDefaultValue("abt")); - assertEquals(1, meta.getPKColumnIndices("tabsync")[0]); - assertEquals("time", meta.getPKColumnNames("tabsync")[0]); - assertEquals(2, meta.getPKColumnIndices("tabsync2")[0]); - assertEquals(1, meta.getPKColumnIndices("tabsync2")[1]); - assertEquals("name", meta.getPKColumnNames("tabsync2")[0]); - assertEquals("life", meta.getPKColumnNames("tabsync2")[1]); - rs.close(); - - // Tests what happens if the result set is closed in a complex query. - try - { - meta.getColumnCount(); - fail("126"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnDisplaySize(1); - fail("127"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnType(1); - fail("128"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnLabel(1); - fail("129"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnTableName(1); - fail("130"); - } - catch (IllegalStateException exception) {} - try - { - meta.getColumnTableName("c"); - fail("131"); - } - catch (IllegalStateException exception) {} - try - { - meta.isNotNull(1); - fail("132"); - } - catch (IllegalStateException exception) {} - try - { - meta.isNotNull("c"); - fail("133"); - } - catch (IllegalStateException exception) {} - try - { - meta.hasDefaultValue(1); - fail("134"); - } - catch (IllegalStateException exception) {} - try - { - meta.hasDefaultValue("c"); - fail("135"); - } - catch (IllegalStateException exception) {} - try - { - meta.getDefaultValue(1); - fail("136"); - } - catch (IllegalStateException exception) {} - try - { - meta.getDefaultValue("c"); - fail("137"); - } - catch (IllegalStateException exception) {} - try - { - meta.getPKColumnIndices("tabsync"); - fail("138"); - } - catch (IllegalStateException exception) {} - try - { - meta.getPKColumnNames("tabsync2"); - fail("139"); - } - catch (IllegalStateException exception) {} - } - - /** - * The test main method. - */ - public void testRun() - { - driver = AllTests.getInstance("Test"); - - // Creates the tables. - try - { - driver.execute("CREATE TABLE tabsync (time LONG primary key default 1, NAME CHAR(5) NOCASE not null)"); - } - catch (AlreadyCreatedException ace) {} - try - { - driver.execute("CREATE TABLE tabsync2 (life LONG, NAME CHAR(5) NOCASE default 'Juli', primary key(name, life))"); - } - catch (AlreadyCreatedException ace) {} - - // Deletes their elements. - try - { - driver.executeUpdate("DELETE tabsync"); - driver.executeUpdate("DELETE tabsync2"); - } - catch (DriverException exception) - { - assertTrue(exception.getMessage().startsWith("It is not possible to open a table within a connection with a different")); - driver.executeUpdate("drop table tabsync"); - driver.executeUpdate("drop table tabsync2"); - driver.execute("CREATE TABLE tabsync (time LONG primary key default 1, NAME CHAR(5) NOCASE not null)"); - driver.execute("CREATE TABLE tabsync2 (life LONG, NAME CHAR(5) NOCASE default 'Juli', primary key(name, life))"); - } - - // Inserts records. - assertEquals(1, driver.executeUpdate("INSERT INTO tabsync VALUES (1, 'Hi')")); - assertEquals(1, driver.executeUpdate("INSERT INTO tabsync2 VALUES (2, 'Hi')")); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - - // Tries to create the tables again. - try - { - driver.execute("CREATE TABLE tabsync (time LONG, NAME CHAR(5) NOCASE)"); - fail("140"); - } - catch (AlreadyCreatedException ace) {} - try - { - driver.execute("CREATE TABLE tabsync2 (life LONG, NAME CHAR(5) NOCASE)"); - fail("141"); - } - catch (AlreadyCreatedException ace) {} - - // Checks that the long value is correct. - assertTrue((rs = driver.executeQuery("SELECT time FROM tabsync")).first()); - assertEquals(1, rs.getRowCount()); - assertEquals(1, rs.getLong(1)); - assertEquals(1, rs.getLong("time")); - rs.close(); - - driver.executeUpdate("DELETE tabsync"); // Empties it. - assertEquals(1, driver.executeUpdate("INSERT INTO tabsync VALUES (2, 'Hi')")); - testMetaData(); // Tests metadata. - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestDeleteAndPurge.java b/LitebaseSDK/src/java/samples/sys/testcases/TestDeleteAndPurge.java deleted file mode 100644 index 9afc9ed20e..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestDeleteAndPurge.java +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests if delete and purge behave as expected. - */ -public class TestDeleteAndPurge extends TestCase -{ - public void testRun() - { - // First inserts the items into a new table. - LitebaseConnection driver = AllTests.getInstance("Test"); - if (driver.exists("CIDADE")) - driver.executeUpdate("drop table CIDADE"); - driver.execute("create table CIDADE(CODIGO int, NOME char(60))"); - PreparedStatement psInsertCidade = driver.prepareStatement("insert into CIDADE(CODIGO, NOME) values(?,?)"); - driver.setRowInc("cidade", 200); - - int i = 200; - while (--i >= 0) - { - psInsertCidade.clearParameters(); - psInsertCidade.setInt(0, i); - psInsertCidade.setString(1,"NOME DA CIDADE " + i); - assertEquals(1, psInsertCidade.executeUpdate()); - } - driver.setRowInc("cidade", -1); - driver.closeAll(); - - // Now deletes and try to insert again. - driver = AllTests.getInstance("Test"); // rnovais@_570_77 - try - { - assertEquals(200, driver.executeUpdate("delete CIDADE")); - } - catch (DriverException exception) // guich@553_10: this error occured when the table name was not being converted to lowercase. - { - fail("Exception thrown: " + exception.getMessage()); - } - assertEquals(200, driver.purge("CIDADE")); - - psInsertCidade = driver.prepareStatement("insert into CIDADE(CODIGO, NOME) values(?,?)"); - driver.setRowInc("cidade", 201); - i = 200; - while (--i >= 0) - { - psInsertCidade.clearParameters(); - psInsertCidade.setInt(0, i); - psInsertCidade.setString(1,"NOME DA CIDADE " + i); - assertEquals(1, psInsertCidade.executeUpdate()); - } - assertEquals(1, psInsertCidade.executeUpdate()); - driver.setRowInc("cidade", -1); - assertEquals(201, driver.getRowCount("CIDADE")); - ResultSet resultSet = driver.executeQuery("select * from cidade where codigo = 0"); - assertEquals(2, resultSet.getRowCount()); - resultSet.close(); - driver.closeAll(); - testRowIdAfterPurge(); - testPruebas(); - } - - /** - * Tests if the rowid values are not wrong. - */ - private void testRowIdAfterPurge() // rnovais@570 - { - LitebaseConnection driver = AllTests.getInstance("Test"); - if (driver.exists("PALM") ) - driver.executeUpdate("drop table PALM"); - driver.execute("create table PALM (cod int)"); - driver.executeUpdate("INSERT INTO palm (cod) values (31)"); - driver.executeUpdate("INSERT INTO palm (cod) values (32)"); - driver.executeUpdate("INSERT INTO palm (cod) values (33)"); - driver.executeUpdate("INSERT INTO palm (cod) values (34)"); - driver.executeUpdate("INSERT INTO palm (cod) values (35)"); - driver.executeUpdate("DELETE FROM palm where palm.rowid=5"); // Deletes the last one. - driver.purge("palm"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.executeUpdate("INSERT INTO palm (cod) values (36)"); - ResultSet rs = driver.executeQuery("SELECT rowid FROM palm where cod = 36"); - assertTrue(rs.next()); - assertEquals("6", rs.getString(1)); - rs.close(); - driver.closeAll(); - } - - /** - * Tests if the table is not corrupted after some purges. - */ - private void testPruebas() // rnovais@570 - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - if (driver.exists("motorCatalog")) - driver.executeUpdate("drop table motorCatalog"); - driver.execute("create table motorCatalog(RFS int, rackID char(5), modelo char(12),serie char(18),error int, descripcion char(255))"); - driver.execute("CREATE INDEX IDX1Mot ON motorCatalog(RFS,rackID)"); - driver.execute("CREATE INDEX IDX3Mot ON motorCatalog(serie)"); - - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1250411014730',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1060511010528',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1040511002208',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1060511010316',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1040511002810',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1040511002458',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1040511003032',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'10900','0G-316-AA','1060511080240',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511163914',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511050258',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511080856',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511081104',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511005248',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511080640',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511080606',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11366','0G-316-AA','1060511080058',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511050050',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1050511152400',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511044944',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511182936',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511050216',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511050618',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511050336',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'11538','0G-316-AA','1060511184118',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1300411051452',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1270411015110',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1300411050724',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1150411180834',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1300411153840',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1300411154840',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1300411011122',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'12500','BG-313-AA','1300411154416',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1020511025150',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1020511023054',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1010511204142',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1020511025354',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1010511204308',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1020511025240',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1020511025534',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13104','BG-314-BA','1010511204640',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1060511164636',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1040511002532',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1050511231210',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1060511080206',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1060511080714',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1040511002922',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1040511003140',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'13919','0G-316-AA','1060511163836',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1060511080748',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1240411183034',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1060511011018',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1060511011318',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1240411183756',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1240411173532',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1240411182922',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14198','0G-316-AA','1060511011352',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1060511045038',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1060511080348',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1050511231410',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1060511081024',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1060511164422',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1040511203358',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1060511164542',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'14443','0G-316-AA','1060511011204',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1060511080132',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1060511010240',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1060511080024',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1060511011242',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1040511002424',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1040511002650',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1040511002256',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15861','0G-316-AA','1060511164120',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1060511163720',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1040511065154',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1060511005542',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1060511163258',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1060511163148',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1240411183902',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1060511163334',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'15872','0G-316-AA','1040511002724',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511182628',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511183120',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511164306',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511045548',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511183858',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1050511151804',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511164344',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'16513','0G-316-AA','1060511165000',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1350411014730',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1360511010528',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511002208',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1360511010316',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511002810',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511002458',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511003032',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1360511080240',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320411014730',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511010528',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511002208',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511010316',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511002810',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511002458',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511003032',0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22222','0G-316-AA','1320511080240',0,'')"); - - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1350411014730')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1360511010528')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1340511002208')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1360511010316')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1340511002810')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1340511002458')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1340511003032')"); - driver.purge("motorCatalog"); - driver.executeUpdate("delete from motorCatalog where (RFS=231635) and (RackID='22221') and (serie='1360511080240')"); - driver.purge("motorCatalog"); - - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1350411014730', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1360511010528', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511002208', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1360511010316', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511002810', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511002458', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1340511003032', " - + "0,'')"); - driver.executeUpdate("INSERT INTO motorCatalog(RFS,rackID,modelo,serie,error,descripcion) values (231635,'22221','0G-316-AA','1360511080240', " - + "0,'')"); - - ResultSet resultSet = driver.executeQuery("select RFS, rackID, modelo,serie ,error , descripcion from motorCatalog where (RFS=231635) and " - + "(RackID='16513')"); - assertEquals(8, resultSet.getRowCount()); - while (resultSet.next()) - assertEquals("16513", resultSet.getString(2)); - resultSet.close(); - - assertEquals(8, (resultSet = driver.executeQuery("select RFS, rackID, modelo,serie ,error , descripcion from motorCatalog where (RFS=231635) " - + "and (RackID='22221')")).getRowCount()); - while (resultSet.next()) - assertEquals("22221", resultSet.getString(2)); - resultSet.close(); - - assertEquals(8, (resultSet = driver.executeQuery("select RFS, rackID, modelo,serie ,error , descripcion from motorCatalog where (RFS=231635) " - + "and (RackID='22222')")).getRowCount()); - while (resultSet.next()) - assertEquals("22222", resultSet.getString(2)); - resultSet.close(); - - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestDrop.java b/LitebaseSDK/src/java/samples/sys/testcases/TestDrop.java deleted file mode 100644 index 998426f06b..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestDrop.java +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.unit.TestCase; - -/** - * Tests drop table and drop index. - */ -public class TestDrop extends TestCase -{ - /** - * The path where the table files are stored. - */ - private String path; - - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - // Gets and normalizes the path. - path = driver.getSourcePath(); - if (!path.endsWith("/")) - path += "/"; - - if (driver.exists("person")) // Drops the table if it exists. - driver.executeUpdate("drop table person"); - - driver.execute("create table person (id int primary key)"); - - // No table file can exist after dropping a table. - driver.executeUpdate("drop table person"); - assertFalse(TableFileExist()); - - driver.execute("create table person (id int)"); - driver.executeUpdate("alter table person add primary key(rowid)"); - - driver.closeAll(); // Closes the table after creating it. - - // No table file can exist after dropping a table. - (driver = AllTests.getInstance("Test")).executeUpdate("drop table person"); - assertFalse(TableFileExist()); - - driver.execute("create table person (id int)"); - driver.executeUpdate("alter table person add primary key (rowid)"); - - try // Only drop primary key can be used to drop a primary key. - { - driver.executeUpdate("drop index rowid on person"); - fail("1"); - } - catch (DriverException exception) {} - assertTrue(IndexExist()); - driver.executeUpdate("alter table person drop primary key"); - assertFalse(IndexExist()); - - driver.executeUpdate("alter table person add primary key (id)"); - - // Closes the table after adding the primary key again. - driver.closeAll(); - driver = AllTests.getInstance("Test"); - - try // Only drop primary key can be used to drop a primary key. - { - driver.executeUpdate("drop index id on person"); - } - catch (DriverException exception) {} - assertTrue(IndexExist()); - driver.executeUpdate("alter table person drop primary key"); - assertFalse(IndexExist()); - - driver.executeUpdate("alter table person add primary key(id)"); - driver.execute("create index idx on person(rowid)"); - - // Only drop primary key can be used to drop a primary key. - assertEquals(1, driver.executeUpdate("drop index * on person")); - assertTrue(IndexExist()); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - assertEquals(0, driver.executeUpdate("drop index * on person")); - driver.executeUpdate("alter table person drop primary key"); - assertFalse(IndexExist()); - - driver.executeUpdate("alter table person add primary key(rowid)"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.executeUpdate("drop index * on person"); - assertTrue(IndexExist()); - driver.executeUpdate("alter table person drop primary key"); - assertFalse(IndexExist()); - - // rowid in the primary key. - driver.executeUpdate("alter table person add primary key(id, rowid)"); - try // Only drop primary key can be used to drop a primary key. - { - driver.executeUpdate("drop index rowid, id on person"); - fail("1"); - } - catch (DriverException exception) {} - assertTrue(IndexExist()); - - try // Only drop primary key can be used to drop a primary key. - { - driver.executeUpdate("drop index id, rowid on person"); - fail("2"); - } - catch (DriverException exception) {} - - driver.executeUpdate("alter table person drop primary key"); - assertFalse(IndexExist()); - - // Closes the table after adding the primary key. - driver.executeUpdate("alter table person add primary key(rowid, id)"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - - try // Only drop primary key can be used to drop a primary key. - { - driver.executeUpdate("drop index id, rowid on person"); - fail("3"); - } - catch (DriverException exception) {} - assertTrue(IndexExist()); - - try // Only drop primary key can be used to drop a primary key. - { - driver.executeUpdate("drop index rowid, id on person"); - fail("4"); - } - catch (DriverException exception) {} - assertTrue(IndexExist()); - - driver.executeUpdate("alter table person drop primary key"); - assertFalse(IndexExist()); - - // Drop table can't erase a table whose name is the prefix of the table being dropped. - if (driver.exists("person2")) // Drops the table if it exists. - driver.executeUpdate("drop table person2"); - driver.execute("create table person2 (id int primary key)"); - driver.executeUpdate("drop table person2"); - assertTrue(TableFileExist()); - - driver.closeAll(); - } - - /** - * Indicates if some table file exists. - * - * @return true if sone table file exists; false, otherwise. - */ - private boolean TableFileExist() - { - try - { - return new File(path + "Test-person.db", File.DONT_OPEN).exists() || new File(path + "Test-person.dbo", File.DONT_OPEN).exists() - || new File(path + "Test-person$0.idk", File.DONT_OPEN).exists() || new File(path + "Test-person$1.idk", File.DONT_OPEN).exists() - || new File(path + "Test-person&1.idk", File.DONT_OPEN).exists() || new File(path + "Test-person&2.idk", File.DONT_OPEN).exists(); - } - catch (IOException exception) - { - return true; - } - } - - /** - * Indicates if some index file exists. - * - * @return true if sone index file exists; false, otherwise. - */ - private boolean IndexExist() - { - try - { - return new File(path + "Test-person$0.idk", File.DONT_OPEN).exists() || new File(path + "Test-person$1.idk", File.DONT_OPEN).exists() - || new File(path + "Test-person&1.idk", File.DONT_OPEN).exists() || new File(path + "Test-person&2.idk", File.DONT_OPEN).exists(); - } - catch (IOException exception) - { - return true; - } - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestDuplicateEntry.java b/LitebaseSDK/src/java/samples/sys/testcases/TestDuplicateEntry.java deleted file mode 100644 index aac743a958..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestDuplicateEntry.java +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; - -/** - * Tests tables with repeated records. - */ -public class TestDuplicateEntry extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - String insert = "INSERT INTO ACT_PRODUTO (ACTCODPRODUTO, ACTCODFABRICANTE, ACTDESCRICAO, ACTQTDEMBALAGEM, ACTCODUNIPRIMARIA, " - + "ACTCODUNIALTERNATIVA, ACTVALORLISTA, ACTTAXAIPI, ACTSTATUS, ACTEMBALAGEMMULTIPLA, ACTFABRICANTEID, ACTLINHAPRODUTOID, " - + "ACTPESOUNITARIO, ACTDATALTERACAO, ACTPRODUTOID, ACTDATINCLUSAO, ACTUSUARIOID, ACTCODIGOBARRAS, ACTCODBARRAUNIDADE, " - + "ACTPESOTOTAL, ACTOBSERVACAO, ACTGRUPOTRIBUTACAOID) VALUES ('1','1', 'EXTRATO DE TOMATE 1', 48, 'null', 'null', 1.570, " - + "2.000, null, null, 1, 1, 140.000, 20040318000000, 2, null, 1, '', 'null', 6720.000, '', null)"; - String delete = "delete act_produto where actcodproduto = '1'"; - ResultSet resultSet; - - try - { - driver.executeUpdate("drop table act_produto"); - } - catch (DriverException exception) {} // Table not found. - - driver.execute("CREATE TABLE act_produto (actcodproduto char(30), actcodfabricante char(30), actdescricao char(70), actqtdembalagem long, " - + "actcoduniprimaria char(5), actcodunialternativa char(5), actvalorlista double, acttaxaipi double, actstatus int, " - + "actembalagemmultipla long, actfabricanteid long, actlinhaprodutoid long, actpesounitario double, actdatalteracao long, " - + "actprodutoid long, actdatinclusao long, actusuarioid long, actcodigobarras char(20), actcodbarraunidade char(30), " - + "actpesototal double, actobservacao char(255), actgrupotributacaoid long)"); - driver.execute("create index idx on act_produto(actpesototal)"); - assertEquals(1, driver.executeUpdate(insert)); - assertEquals(1, driver.executeUpdate(insert)); - assertEquals(2, driver.executeUpdate(delete)); - assertEquals(1, driver.executeUpdate(insert)); - assertEquals(1, driver.executeUpdate("delete act_produto")); - assertEquals(0, driver.executeUpdate(delete)); - assertEquals(1, driver.executeUpdate(insert)); - - try - { - assertNotNull(resultSet = driver.executeQuery("select * from act_produto where ACTGRUPOTRIBUTACAOID = 20050411095951L")); - resultSet.close(); - } - catch (SQLParseException exception) - { - if (Settings.onJavaSE) - exception.printStackTrace(); - fail("Exception thrown: " + exception.getMessage()); - } - - // Tests the purge for tables with big headers. - driver.purge("act_produto"); - resultSet = driver.executeQuery("select rowid from act_produto"); - assertTrue(resultSet.next()); - assertEquals(resultSet.getInt("rowid"), 4); - assertFalse(resultSet.next()); - resultSet.close(); - - driver.closeAll(); - testRepetedNameOnSelect(); - } - - /** - * Tests duplicated name on the select clause. - */ - private void testRepetedNameOnSelect() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - - // Creates the table and stores data. - driver.execute("create table person(name char(5), age int)"); - driver.executeUpdate("Insert into person values ('RLN', 20)"); - driver.executeUpdate("Insert into person values ('IOG', 18)"); - - try - { - ResultSet rs = driver.executeQuery("Select name, name as t, age from person"); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("t")); - assertEquals(20, rs.getInt("age")); - assertEquals("RLN\tRLN\t20", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IOG", rs.getString("t")); - assertEquals(18, rs.getInt("age")); - assertEquals("IOG\tIOG\t18", rs.rowToString()); - rs.close(); - } - catch (RuntimeException exception) - { - fail(); - } - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestEndianess.java b/LitebaseSDK/src/java/samples/sys/testcases/TestEndianess.java deleted file mode 100644 index b0aed8d56e..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestEndianess.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; - -/** - * This one tests if the endianess is correct on all platforms. - */ -public class TestEndianess extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - if (driver.exists("testendianess")) - driver.executeUpdate("drop table testendianess"); - - driver.execute("create table testendianess(ID_PRODUTO int , ID_FORNECEDOR int, NM_NOME char(30), VL_VALOR double, vl_long long, " - + "vl_float float) "); - driver.executeUpdate("insert into testendianess values (10 ,20,'guilherme',3.14D," + 0x1234567890123456L + ",3.15f)"); - - - ResultSet rs = driver.executeQuery("select * from testendianess where ID_PRODUTO = 10"); - assertTrue(rs.next()); - rs.setDecimalPlaces(4, 2); - rs.setDecimalPlaces(6, 2); - - // Tests double. - double d = rs.getDouble("VL_VALOR"); - assertEquals(3.14d, d, 0.01); - assertEquals("3.14", Convert.toString(d, 2)); - assertEquals("3.14", rs.getString("vl_valor")); - - // Tests float, - double f = (double)rs.getFloat("VL_float"); - assertEquals(3.15, f, 0.01); - assertEquals("3.15", Convert.toString(f,2)); - assertEquals("3.15", rs.getString("vl_float")); - - // Tests long. - long l = rs.getLong("VL_long"); - assertEquals(0x1234567890123456L, l); - assertEquals(Convert.toString(0x1234567890123456L), Convert.toString(l)); - assertEquals(Convert.toString(0x1234567890123456L), rs.getString("vl_long")); - - rs.close(); - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestIndexIneqAndLike.java b/LitebaseSDK/src/java/samples/sys/testcases/TestIndexIneqAndLike.java deleted file mode 100644 index cfb070c017..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestIndexIneqAndLike.java +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests the use of indices with like and - * catch error found in 555_3 - */ -public class TestIndexIneqAndLike extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - int n = 120; - if (driver.exists("cliente")) - driver.executeUpdate("drop table cliente"); - driver.execute("create table CLIENTE(CODIGO int, NOME char(60) NOCASE)"); - driver.execute("CREATE INDEX IDX ON cliente(NoME)"); - driver.execute("CREATE INDEX IDX ON cliente(codigo)"); - PreparedStatement ps = driver.prepareStatement("insert into cliente values (?,?)"); - StringBuffer sb = new StringBuffer("nOme"); - driver.setRowInc("cliente", n); - int i = 120; - while (--i >= 0) - if (i % 10 != 0) - { - sb.setLength(4); - ps.setInt(0, i); - ps.setString(1, sb.append(i).toString()); - ps.executeUpdate(); - } - driver.setRowInc("cliente", -1); - - // Select using like. - testResult(driver.executeQuery("select * from cliente where nome like 'nome2%'"), 10); - testResult(driver.executeQuery("select * from cliente where nome like 'Nome1%'"), 28); - - // Select using not not like. - testResult(driver.executeQuery("select * from cliente where not nome not like 'nome2%'"), 10); - testResult(driver.executeQuery("select * from cliente where not nome not like 'Nome1%'"), 28); - - // Select using inequalities. - testResult(driver.executeQuery("select * from cliente where codigo >= 26 and codigo <= 36"), 10); - testResult(driver.executeQuery("select * from cliente where not codigo < 26 and not codigo > 36"), 10); - testResult(driver.executeQuery("select * from cliente where not (codigo < 26 or codigo > 36)"), 10); - testResult(driver.executeQuery("select * from cliente where codigo > 26 and codigo <= 36"), 9); - testResult(driver.executeQuery("select * from cliente where not codigo <= 26 and not codigo > 36"), 9); - testResult(driver.executeQuery("select * from cliente where not (codigo <= 26 or codigo > 36)"), 9); - testResult(driver.executeQuery("select * from cliente where codigo > 26 and codigo < 36"), 8); - testResult(driver.executeQuery("select * from cliente where codigo >= 26"), 85); - testResult(driver.executeQuery("select * from cliente where codigo > 26"), 84); - testResult(driver.executeQuery("select * from cliente where codigo <= 26"), 24); - testResult(driver.executeQuery("select * from cliente where codigo < 26"), 23); - - // nOme1, nOme2, nOme11-nOme19, nOme101-nOme109, nOme111-nOme119 - testResult(driver.executeQuery("select * from cliente where nome < 'Nome20'"),29); - - driver.closeAll(); - } - - /** - * Tests if the result set returned the expected number of rows. - * - * @param rs The result set. - * @param expected The number of rows expected. - */ - private void testResult(ResultSet rs, int expected) - { - int returned = rs.getRowCount(); - - if (expected != returned) - { - while (rs.next()) - output(rs.getString(1) + " / " + rs.getString(2)); - assertEquals(expected, returned); - } - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestIndexRebalance.java b/LitebaseSDK/src/java/samples/sys/testcases/TestIndexRebalance.java deleted file mode 100644 index 0e824b1cbe..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestIndexRebalance.java +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; -import totalcross.util.*; - -/** - * Tests indices with a lot of nodes. - */ -public class TestIndexRebalance extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - if (driver.exists("CLIENTE")) - driver.executeUpdate("drop table cliente"); - driver.execute("create table CLIENTE(CODIGO int primary key, NOME char(60), FANTASIA char(60),BAIRRO int)"); - driver.execute("create index IDX_CLIENTE_BAIRRO on CLIENTE(BAIRRO)"); - driver.execute("create index IDX_CLIENTE_NOME on CLIENTE(NOME)"); - driver.execute("create index IDX_CLIENTE_FANTASIA on CLIENTE(FANTASIA)"); - driver.execute("create index IDX_ROWID on CLIENTE(rowid)"); - driver.setRowInc("CLIENTE", 1125); // Requires two increments to fill all the 2250 rows. - PreparedStatement psInsertCliente = driver.prepareStatement("insert into CLIENTE(CODIGO, NOME, FANTASIA, BAIRRO) values (?, ?, ?, ?)"); - PreparedStatement psDelete = driver.prepareStatement("delete from cliente where codigo=?"); - int i = -1; - psInsertCliente.setInt(3, 2); - try - { - Random r = new Random(100331); - IntHashtable keys = new IntHashtable(8333); - while (++i < 2250) // 250 of them will be deleted. - { - if (i % 100 == 0) - status(Convert.toString(i).concat(" out of 2250")); - - int k; - while (true) // make sure that a key that was not repeated will be got. - { - k = r.between(0, 100000); - if ((k & 1) == 0) - k = -k; - if (!keys.exists(k)) - { - keys.put(k,k); - break; - } - } - psInsertCliente.setInt(0, k); - psInsertCliente.setString(1,"NOME " + k); - psInsertCliente.setString(2,"FANTASIA DO CLIENTE " + i); - - assertEquals(1, psInsertCliente.executeUpdate()); - if ((k & 7) == 0) // force some deletions - { - psDelete.setInt(0,k); - assertEquals(1, psDelete.executeUpdate()); - try // Lets the key be used again - { - keys.remove(k); - } - catch (ElementNotFoundException exception) {} - } - - } - status(""); - } - catch (PrimaryKeyViolationException exception) - { - fail("Primary key violation occured at " + i); - } - try // Tries to insert again the same last client. - { - psInsertCliente.executeUpdate(); - fail("Primary key not violated!"); - } - catch (PrimaryKeyViolationException exception) {} // Test ok. - - driver.setRowInc("cliente", -1); // Sets rowInc to default. - ResultSet resultSet = driver.executeQuery("select * from cliente where bairro = 2"); - assertEquals(1960, resultSet.getRowCount()); - resultSet.close(); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - assertEquals(1960, (resultSet = driver.executeQuery("select * from cliente where bairro >= 2")).getRowCount()); - resultSet.close(); - int tempo = Vm.getTimeStamp(); - driver.purge("cliente"); - output("Purge took " + (Vm.getTimeStamp() - tempo) + " ms."); - driver.execute("create index IDX on CLIENTE(rowid, nome)"); - assertEquals(1960, (resultSet = driver.executeQuery("select * from cliente where bairro <= 2")).getRowCount()); - resultSet.close(); - assertEquals(1960, driver.executeUpdate("update cliente set bairro = 3")); - assertEquals(1960, (resultSet = driver.executeQuery("select * from cliente where bairro <= 3 and bairro >= 3")).getRowCount()); - resultSet.close(); - assertEquals(1960, (resultSet = driver.executeQuery("select * from cliente where nome like 'NOME %'")).getRowCount()); - resultSet.close(); - assertEquals(1960, driver.executeUpdate("delete from cliente")); - driver.executeUpdate("drop table cliente"); - - // Solved a index bug which could cause its corruption. - if (driver.exists("t")) - driver.executeUpdate("drop table t"); - driver.execute("create table t (x short)"); - driver.execute("create index idx on t(x)"); - i = 30; - while (--i >= 0) - driver.executeUpdate("insert into t values (1)"); - driver.executeUpdate("delete from t where rowid < 16"); - driver.executeUpdate("delete from t where rowid > 16"); - driver.executeUpdate("insert into t values (1)"); - driver.executeUpdate("delete from t where rowid = 16"); - - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestInvalidArguments.java b/LitebaseSDK/src/java/samples/sys/testcases/TestInvalidArguments.java deleted file mode 100644 index d4ec9c9d2c..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestInvalidArguments.java +++ /dev/null @@ -1,1388 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.sys.Convert; -import totalcross.sys.Settings; -import totalcross.unit.TestCase; - -/** - * Tests Litebase methods with invalid arguments. This is necessary to test control conditions. - */ -public class TestInvalidArguments extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - testNullString(driver); // Tests null strings passed to LitebaseConnection methods. - testEmptyString(driver); // Tests empty strings passed to LitebaseConnection methods. - testLexicalError(driver); // Tests sql commands with lexical errors. - testDuplicatedColumn(driver); // Tests table creations with duplicated column names. - testLongTableName(driver); // Tests too long table names. - testTooManyColumns(driver); // Tests the use of tables with too many columns. - testInvalidCreateIndex(driver); // Tests a create index on a non-existing column. - testWrongSyntax(driver); // Tests some syntaticaly wrong sql commands. - testNonExistingTable(driver); // Tests Litebase commands that use non-existing tables. - testNonExistingColumn(driver); // Tests Litebase SQLs that use unknown columns. - testWrongInsertUpdate(driver); // Tests wrong inserts and updates. - testTooLargeNumbers(driver); // Tests some large numbers greater than the declared type. - testWrongNumberTypesInWhere(driver); // Tests bigger strings and wrong types in the where clause. - testInvalidInc(driver); // Tests invalid increments. - testInvalidRowidAlter(driver); // Tries to alter the rowid. - testTooBigSelect(driver); // Tries to do a select * with too many fields. - driver.closeAll(); - testLongPath(); // Tests too long paths. - testInvalidCrid(); // Tests invalid application id sizes. - testInvalidParameter(); // Tests invalid connection parameter. - } - - /** - * Tests null strings passed to LitebaseConnection methods. - * - * @param driver The connection with Litebase. - */ - private void testNullString(LitebaseConnection driver) - { - try - { - driver.getRowCount(null); // juliana@201_31 - fail("1"); - } - catch (NullPointerException exception) {} - - try - { - driver.execute(null); - fail("2"); - } - catch (NullPointerException exception) {} - - try - { - driver.executeQuery(null); - fail("3"); - } - catch (NullPointerException exception) {} - - try - { - driver.executeUpdate(null); - fail("4"); - } - catch (NullPointerException exception) {} - - try - { - driver.exists(null); - fail("5"); - } - catch (NullPointerException exception) {} - - try - { - driver.getCurrentRowId(null); - fail("6"); - } - catch (NullPointerException exception) {} - - try - { - driver.getRowCountDeleted(null); - fail("7"); - } - catch (NullPointerException exception) {} - - try - { - driver.getRowIterator(null); - fail("8"); - } - catch (NullPointerException exception) {} - - try - { - driver.prepareStatement(null); - fail("9"); - } - catch (NullPointerException exception) {} - - try - { - driver.purge(null); - fail("10"); - } - catch (NullPointerException exception) {} - - try - { - driver.setRowInc(null, -1); - fail("11"); - } - catch (NullPointerException exception) {} - - try - { - driver.isOpen(null); - fail("12"); - } - catch (NullPointerException exception) {} - - try - { - LitebaseConnection.dropDatabase(null, driver.getSourcePath(), -1); - fail("13"); - } - catch (NullPointerException exception) {} - catch (DriverException exception) {} - - try - { - LitebaseConnection.dropDatabase("Test", null, -1); - fail("14"); - } - catch (NullPointerException exception) {} - } - - /** - * Tests empty strings passed to LitebaseConnection methods. - * - * @param driver The connection with Litebase. - */ - private void testEmptyString(LitebaseConnection driver) - { - assertFalse(driver.exists("")); // juliana@201_31: an empty table can't exist. - - try - { - driver.getRowCount(""); - fail("15"); - } - catch (DriverException exception) - { - exception.printStackTrace(); - } - - try - { - driver.execute(""); - fail("16"); - } - catch (SQLParseException exception) {} - - try - { - driver.executeQuery(""); - fail("17"); - } - catch (SQLParseException exception) {} - - try - { - driver.executeUpdate(""); - fail("18"); - } - catch (SQLParseException exception) {} - - try - { - driver.getCurrentRowId(""); - fail("19"); - } - catch (DriverException exception) {} - - try - { - driver.getRowCountDeleted(""); - fail("20"); - } - catch (DriverException exception) {} - - try - { - driver.getRowIterator(""); - fail("21"); - } - catch (DriverException exception) {} - - try - { - driver.prepareStatement(""); - fail("22"); - } - catch (SQLParseException exception) {} - - try - { - driver.purge(""); - fail("23"); - } - catch (DriverException exception) {} - - try - { - driver.setRowInc("", -1); - fail("24"); - } - catch (DriverException exception) {} - - assertFalse(driver.isOpen("")); - - try - { - LitebaseConnection.dropDatabase("", driver.getSourcePath(), -1); - fail("25"); - } - catch (DriverException exception) {} - - try - { - LitebaseConnection.dropDatabase("Test", "", -1); - fail("26"); - } - catch (DriverException exception) {} - } - - /** - * Tests sql commands with lexical errors. - * - * @param driver The connection with Litebase. - */ - private void testLexicalError(LitebaseConnection driver) - { - try // Inexistent character. - { - driver.execute("%"); - fail("27"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.execute("!"); - fail("28"); - } - catch (SQLParseException exception) {} - - try // Inexistent token. - { - driver.execute("=!"); - fail("29"); - } - catch (SQLParseException exception) {} - - try // Inexistent token. - { - driver.execute("><"); - fail("30"); - } - catch (SQLParseException exception) {} - - try // A single quote can't be in a string. - { - driver.execute("'''"); - fail("31"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select *! from tab where x > 5"); - fail("32"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select !* from tab where x > 5"); - fail("33"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select * from ! tab where x > 5"); - fail("34"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select * from tab ! where x > 5"); - fail("35"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select * from tab where !x > 5"); - fail("36"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select * from tab where x >! 5"); - fail("37"); - } - catch (SQLParseException exception) {} - - try // Inexistent character. - { - driver.executeQuery("select * from tab where x > 5!"); - fail("38"); - } - catch (SQLParseException exception) {} - - try // Invalid identifier. - { - driver.executeQuery("select * from -tab where x > 5"); - fail("39"); - } - catch (SQLParseException exception) {} - - try // Invalid number. - { - driver.executeQuery("select * from tab where x > 5i"); - fail("40"); - } - catch (SQLParseException exception) {} - } - - /** - * Tests table creations with duplicated column names. - * - * @param driver The connection with Litebase. - */ - private void testDuplicatedColumn(LitebaseConnection driver) - { - if (driver.exists("t")) - driver.executeUpdate("drop table t"); - - try // Repeated x. - { - driver.execute("create table t (x int, x char(5))"); - fail("41"); - } - catch (SQLParseException exception) {} - - try // Repeated x. - { - driver.execute("create table t (x int, y int, x char(5))"); - fail("42"); - } - catch (SQLParseException exception) {} - - try // Repeated x - { - driver.execute("create table t (y int, x int, x char(5))"); - fail("43"); - } - catch (SQLParseException exception) {} - - try // rowid is already used as a column. - { - driver.execute("create table t (rowid int)"); - fail("44"); - } - catch (SQLParseException exception) {} - - try // rowid is already used as a column. - { - driver.execute("create table t (x int, rowid int)"); - fail("45"); - } - catch (SQLParseException exception) {} - - try // rowid is already used as a column. - { - driver.execute("create table t (rowid int, x char(5))"); - fail("46"); - } - catch (SQLParseException exception) {} - } - - /** - * Tests too long table names. - * - * @param driver The connection with Litebase. - */ - private void testLongTableName(LitebaseConnection driver) - { - try // It is possible to create a table with the maximum table name length. - { - if (driver.exists("R1234567891234567890123")) - driver.executeUpdate("drop table R1234567891234567890123"); - driver.execute("create table R1234567891234567890123 (FIRST_NAME CHAR(30) PRIMARY KEY, CITY CHAR(30) )"); - } - catch (SQLParseException exception) - { - fail("47"); - } - - // rnovais@570_114: Tries to create tables with a name longer than the max size (23). - try // Tries to create one with (max size + 1) = 24 characters. - { - driver.execute("create table R12345678912345678901234 (FIRST_NAME CHAR(30) PRIMARY KEY, CITY CHAR(30) )"); - fail("48"); - } - catch (SQLParseException exception) {} - - try // Tries to create one with 41 characters. - { - driver.execute("create table R12345678901234567890123456789012345678901234567890 (FIRST_NAME CHAR(30) PRIMARY KEY, CITY CHAR(30) )"); - fail("49"); - } - catch (SQLParseException exception) {} - - try // Tries to create one with 51 characters. - { - driver.execute( - "create table R123456789012345678901234567890123456789012345678901234567890 (FIRST_NAME CHAR(30) PRIMARY KEY, CITY CHAR(30) )"); - fail("50"); - } - catch (SQLParseException exception) {} - - try // It is possible to use a table with max length. - { - driver.executeUpdate("insert into R1234567891234567890123 values('juliana', 'Rio de Janeiro')"); - } - catch (SQLParseException exception) - { - fail("51"); - } - - // It is not possible to use tables larger then the maximum length. - try // Tries to drop one with (max size + 1) = 24 characters. - { - driver.executeUpdate("drop table R12345678912345678901234"); - fail("52"); - } - catch (DriverException exception) {} - - try // Tries to do an insert in a table with 41 characters. - { - driver.executeUpdate("insert into R12345678901234567890123456789012345678901234567890 values('juliana', 'Rio de Janeiro')"); - fail("53"); - } - catch (DriverException exception) {} - - try // Tries to do a select in a table with 51 characters. - { - driver.executeQuery("select * from R123456789012345678901234567890123456789012345678901234567890"); - fail("54"); - } - catch (DriverException exception) {} - } - - /** - * Tests Invalid application Id sizes. - */ - private void testInvalidCrid() - { - try // Null - { - AllTests.getInstance(null); - fail("55"); - } - catch (NullPointerException exception) {} - try // size 0 != 4 - { - AllTests.getInstance(""); - fail("56"); - } - catch (DriverException exception) {} - try // size 1 != 4 - { - AllTests.getInstance("T"); - fail("57"); - } - catch (DriverException exception) {} - try // size 2 != 4 - { - AllTests.getInstance("Te"); - fail("58"); - } - catch (DriverException exception) {} - try // size 3 != 4 - { - AllTests.getInstance("Tes"); - fail("59"); - } - catch (DriverException exception) {} - try // size 5 != 4 - { - AllTests.getInstance("Tests"); - fail("60"); - } - catch (DriverException exception) {} - try // size 6 != 4 - { - AllTests.getInstance("2Tests"); - fail("61"); - } - catch (DriverException exception) {} - } - - /** - * Tests invalid increments. - * - * @param driver The connection with Litebase. - */ - private void testInvalidInc(LitebaseConnection driver) - { - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - driver.execute("create table person (id int)"); - try - { - driver.setRowInc("person", 0); - fail("62"); - } - catch (IllegalArgumentException exception) {} - try - { - driver.setRowInc("person", -2); - fail("63"); - } - catch (IllegalArgumentException exception) {} - } - - /** - * Tests the use of tables with too many columns. - * - * @param driver The connection with Litebase. - */ - private void testTooManyColumns(LitebaseConnection driver) - { - StringBuffer sBuffer = new StringBuffer(3916); - - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - - sBuffer.append("create table person (a0 int"); - int i = 0; - while (++i < 255) - sBuffer.append(", a").append(i).append(" int"); - sBuffer.append(")"); - - try // Tries to create a table with too many columns. - { - driver.execute(sBuffer.toString()); - fail("64"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - sBuffer.setLength(0); - sBuffer.append("create table person ("); - i = -1; - while (++i < 254) - sBuffer.append("a").append(i).append(" int, "); - sBuffer.append("primary key(rowid"); - i = -1; - while (++i < 254) - sBuffer.append(", a").append(i); - sBuffer.append("))"); - - try // Tries to create a table with too many columns in the composed primary key. - { - driver.execute(sBuffer.toString()); - fail("65"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - // Too many composed indices. - driver.execute("create table person (q0 int, q1 int, q2 int, q3 int, q4 int, w0 int, w1 int, w2 int, w3 int, w4 int," - + "e0 int, e1 int, e2 int, e3 int, e4 int, r0 int, r1 int, r2 int, r3 int, r4 int, t0 int, t1 int, t2 int, t3 int, t4 int," - + "y0 int, y1 int, y2 int, y3 int, y4 int, u0 int, u1 int, u2 int, u3 int, u4 int, i0 int, i1 int, i2 int, i3 int, i4 int," - + "o0 int, o1 int, o2 int, o3 int, o4 int, p0 int, p1 int, p2 int, p3 int, p4 int, a0 int, a1 int, a2 int, a3 int, a4 int," - + "s0 int, s1 int, s2 int, s3 int, s4 int, d0 int, d1 int, d2 int, d3 int, d4 int, f0 int, f1 int, f2 int, f3 int, f4 int," - + "g0 int, g1 int, g2 int, g3 int, g4 int, h0 int, h1 int, h2 int, h3 int, h4 int, j0 int, j1 int, j2 int, j3 int, j4 int," - + "k0 int, k1 int, k2 int, k3 int, k4 int, l0 int, l1 int, l2 int, l3 int, l4 int, z0 int, z1 int, z2 int, z3 int, z4 int," - + "x0 int, x1 int, x2 int, x3 int, x4 int, c0 int, c1 int, c2 int, c3 int, c4 int, v0 int, v1 int, v2 int, v3 int, v4 int," - + "b0 int, b1 int, b2 int, b3 int, b4 int, n0 int, n1 int, n2 int, n3 int, n4 int, m0 int, m1 int, m2 int)"); - - driver.execute("create index idx on person (rowid, q0)"); - driver.execute("create index idx on person (rowid, q1)"); - driver.execute("create index idx on person (rowid, q2)"); - driver.execute("create index idx on person (rowid, q3)"); - driver.execute("create index idx on person (rowid, q4)"); - driver.execute("create index idx on person (rowid, w0)"); - driver.execute("create index idx on person (rowid, w1)"); - driver.execute("create index idx on person (rowid, w2)"); - driver.execute("create index idx on person (rowid, w3)"); - driver.execute("create index idx on person (rowid, w4)"); - driver.execute("create index idx on person (rowid, e0)"); - driver.execute("create index idx on person (rowid, e1)"); - driver.execute("create index idx on person (rowid, e2)"); - driver.execute("create index idx on person (rowid, e3)"); - driver.execute("create index idx on person (rowid, e4)"); - driver.execute("create index idx on person (rowid, r0)"); - driver.execute("create index idx on person (rowid, r1)"); - driver.execute("create index idx on person (rowid, r2)"); - driver.execute("create index idx on person (rowid, r3)"); - driver.execute("create index idx on person (rowid, r4)"); - driver.execute("create index idx on person (rowid, t0)"); - driver.execute("create index idx on person (rowid, t1)"); - driver.execute("create index idx on person (rowid, t2)"); - driver.execute("create index idx on person (rowid, t3)"); - driver.execute("create index idx on person (rowid, t4)"); - driver.execute("create index idx on person (rowid, y0)"); - driver.execute("create index idx on person (rowid, y1)"); - driver.execute("create index idx on person (rowid, y2)"); - driver.execute("create index idx on person (rowid, y3)"); - driver.execute("create index idx on person (rowid, y4)"); - driver.execute("create index idx on person (rowid, u0)"); - driver.execute("create index idx on person (rowid, u1)"); - - try // The 33� composed index creation will fail. - { - driver.execute("create index idx on person (rowid, u2)"); - fail("66"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (DriverException exception) {} - - sBuffer.setLength(0); - sBuffer.append("insert into person values (?"); - i = 0; - while (++i < 255) - sBuffer.append(", ?"); - sBuffer.append(')'); - - // Too many columns to be inserted. - try - { - driver.prepareStatement(sBuffer.toString()); - fail("67"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - sBuffer.setLength(0); - sBuffer.append("insert into person values (0"); - i = 0; - while (++i < 255) - sBuffer.append(", 0"); - sBuffer.append(')'); - - try - { - driver.executeUpdate(sBuffer.toString()); - fail("68"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - sBuffer.setLength(0); - sBuffer.append("update person set a0 = ?"); - i = 0; - while (++i < 255) - sBuffer.append(", a").append(i).append(" = ?"); - sBuffer.append(')'); - - // Too many columns to be updated. - try - { - driver.prepareStatement(sBuffer.toString()); - fail("69"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - sBuffer.setLength(0); - sBuffer.append("update person set a0 = 0"); - i = 0; - while (++i < 255) - sBuffer.append(", a").append(i).append(" = 0"); - sBuffer.append(')'); - - try - { - driver.executeUpdate(sBuffer.toString()); - fail("70"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - sBuffer.setLength(0); - sBuffer.append("select * from person where a0 = ?"); - i = -1; - while (++i < 254) - sBuffer.append(" and a").append(i).append(" = ?"); - - // Too many columns to be selected. - try - { - driver.prepareStatement(sBuffer.toString()); - fail("71"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - - sBuffer.setLength(0); - sBuffer.append("select * from person where a0 = 0"); - i = -1; - while (++i < 254) - sBuffer.append(" and a").append(i).append(" = 0"); - - try - { - driver.executeQuery(sBuffer.toString()); - fail("72"); - } - catch (ArrayIndexOutOfBoundsException exception) {} - catch (SQLParseException exception) {} - } - - /** - * Tests a create index on a non-existing column. - * - * @param driver The connection with Litebase. - */ - private void testInvalidCreateIndex(LitebaseConnection driver) - { - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - driver.execute("create table person(id int primary key, lastname char(20), firstname char(20), age int, dept int)"); - try - { - driver.execute("create index nocolumnindex on person(nocolumn)"); - fail("73"); - } - catch (DriverException exception) {} - } - - /** - * Tests some syntaticaly wrong sql commands. - * - * @param driver The connection with Litebase. - */ - private void testWrongSyntax(LitebaseConnection driver) - { - try - { - driver.execute("create table invalidsql XXXXXXXXXXXXXXXXXXXXXXXXXXXX"); - fail("74"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create index invalidindex"); - fail("75"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("alter table employee"); - fail("76"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("drop tablexxxxxx"); - fail("77"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("drop index xxxxxxxx"); - fail("78"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into employee () values()"); - fail("79"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("delete data from employee where id = 99"); - fail("80"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("update lastname='updated' where id = 99"); - fail("81"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("update lastname='updated' where id = 99"); - fail("82"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("select table employee"); - fail("83"); - } - catch (SQLParseException exception) {} - try - { - driver.prepareStatement("select employee where id = ?"); - fail("84"); - } - catch (SQLParseException exception) {} - } - - /** - * Tests sql commands that use non-existing tables. - * - * @param driver The connection with Litebase. - */ - private void testNonExistingTable(LitebaseConnection driver) - { - try - { - driver.execute("create index deptindex on invalidtable(dept)"); - fail("85"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("alter table notable drop primary key"); - fail("86"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("drop table company"); - fail("87"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("drop index deptindex on notable"); - fail("88"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("insert into company(id,name) values (1,'name')"); - fail("89"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("delete from company"); - fail("90"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("update company set name='updated'"); - fail("91"); - } - catch (DriverException exception) {} - try - { - driver.executeQuery("select * from company"); - fail("92"); - } - catch (DriverException exception) {} - try - { - driver.purge("company"); - fail("93"); - } - catch (DriverException exception) {} - try - { - driver.convert("company"); - fail("94"); - } - catch (DriverException exception) {} - try - { - driver.getRowCount("company"); - fail("95"); - } - catch (DriverException exception) {} - try - { - driver.getCurrentRowId("company"); - fail("96"); - } - catch (DriverException exception) {} - try - { - driver.getRowCountDeleted("company"); - fail("97"); - } - catch (DriverException exception) {} - try - { - driver.getRowIterator("company"); - fail("98"); - } - catch (DriverException exception) {} - try - { - driver.recoverTable("company"); - fail("99"); - } - catch (DriverException exception) {} - try - { - driver.setRowInc("company", -1); - fail("100"); - } - catch (DriverException exception) {} - } - - /** - * Tests Litebase SQLs that use unknown columns. - * - * @param driver The connection with Litebase. - */ - private void testNonExistingColumn(LitebaseConnection driver) - { - if (driver.exists("employee")) - driver.executeUpdate("drop table employee"); - driver.execute("create table employee(id int, lastname char(20), firstname char(20), age int, dept int)"); - - try - { - driver.execute("create index nocolumnindex on employee(nocolumn)"); - fail("101"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("alter table employee add primary key(nocolumn) "); - fail("102"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("alter table employee rename nocolumn to newcolumn"); - fail("103"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("drop index firstname on employee"); - fail("104"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("insert into employee (id,lastname,firstname,years,dept,nocolumn) values(1,'lname1','fname1',1,1,'nocolumn')"); - fail("105"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("delete from employee where gender = 'M'"); - fail("106"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("update employee set middlename='updated' where id = 99"); - fail("107"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("select middlename from employee"); - fail("108"); - } - catch (SQLParseException exception) {} - } - - /** - * Tests some large numbers greater than the declared type. - * - * @param driver The connection with Litebase. - */ - private void testTooLargeNumbers(LitebaseConnection driver) - { - if (driver.exists("bignumbers")) - driver.executeUpdate("drop table bignumbers"); - driver.execute("create table bignumbers (s short, i int, l long, c char(1))"); - - // Invalid short values. - try - { - driver.executeQuery("select * from bignumbers where s = 32768"); - fail("109"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("select * from bignumbers where s = -32769"); - fail("110"); - } - catch (SQLParseException exception) {} - - // Invalid int values. - try - { - driver.executeQuery("select * from bignumbers where i = 2147483648"); - fail("111"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("select * from bignumbers where i = -2147483649"); - fail("112"); - } - catch (SQLParseException exception) {} - - // Invalid long values. - try - { - driver.executeQuery("select * from bignumbers where l = 9223372036854775808"); - fail("113"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("select * from bignumbers where l = -9223372036854775809"); - fail("114"); - } - catch (SQLParseException exception) {} - - // Valid numeric values. - driver.executeQuery("select * from bignumbers where s = +32767").close(); - driver.executeQuery("select * from bignumbers where s = -32768").close(); - driver.executeQuery("select * from bignumbers where i = +2147483647").close(); - driver.executeQuery("select * from bignumbers where i = -2147483648").close(); - driver.executeQuery("select * from bignumbers where l = +9223372036854775807").close(); - driver.executeQuery("select * from bignumbers where l = -9223372036854775808").close(); - - // Invalid short values. - try - { - driver.executeUpdate("insert into bignumbers (s) values (32768)"); - fail("115"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into bignumbers (s) values (-32769)"); - fail("116"); - } - catch (SQLParseException exception) {} - - // Invalid int values. - try - { - driver.executeUpdate("insert into bignumbers (i) values (2147483648)"); - fail("117"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into bignumbers (i) values (-2147483649)"); - fail("118"); - } - catch (SQLParseException exception) {} - - // Invalid long values. - try - { - driver.executeUpdate("insert into bignumbers (l) values (9223372036854775808)"); - fail("119"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into bignumbers (l) values (-9223372036854775809)"); - fail("120"); - } - catch (SQLParseException exception) {} - - // Valid numeric values. - driver.executeUpdate("insert into bignumbers values (+32767, +2147483647, +9223372036854775807, 'j')"); - driver.executeUpdate("insert into bignumbers values (-32768, -2147483648, -9223372036854775808, 'i')"); - - // Tests valid numeric values insertion. - ResultSet resultSet = driver.executeQuery("select * from bignumbers"); - assertTrue(resultSet.next()); - assertEquals(32767, resultSet.getShort("s")); - assertEquals(2147483647, resultSet.getInt("i")); - assertEquals(9223372036854775807L, resultSet.getLong("l")); - assertTrue(resultSet.next()); - assertEquals(-32768, resultSet.getShort("s")); - assertEquals(-2147483648, resultSet.getInt("i")); - assertEquals(-9223372036854775808L, resultSet.getLong("l")); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Tests bigger strings and wrong types in the where clause. - * - * @param driver The connection with Litebase. - */ - private void testWrongNumberTypesInWhere(LitebaseConnection driver) - { - // Invalid numeric types. - PreparedStatement prepStmt = driver.prepareStatement("select * from bignumbers where s = ?"); - try - { - prepStmt.setInt(0, 0); - fail("121"); - } - catch (DriverException exception) {} - try - { - driver.executeQuery("select * from bignumbers where s = 0.0"); - fail("122"); - } - catch (SQLParseException exception) {} - - // Invalid number. - try - { - driver.executeQuery("select * from bignumbers where s = 'juliana'"); - fail("123"); - } - catch (SQLParseException exception) {} - - // This can't crash Litebase. - driver.executeQuery("select * from bignumbers where c = 'Juliana Carpes Imperial'").close(); - } - - /** - * Tests wrong inserts and updates. - * - * @param driver The connection with Litebase. - */ - private void testWrongInsertUpdate(LitebaseConnection driver) - { - try - { - driver.executeUpdate("insert into employee (id,lastname) values (101,'lname101','fname101',101,1)"); - fail("124"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("insert into employee (id,lastname) values ('lname101', 101)"); - fail("125"); - } - catch (SQLParseException exception) {} - - try - { - driver.executeUpdate("update employee set middlename='updated' where id = 99"); - fail("126"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("update employee set id = 'lname101', lastname = 101)"); - fail("127"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("update employee set id = 'lname101', id = 'lname101', id = 'lname101', id = 'lname101', id = 'lname101', id = 'lname101'"); - fail("128"); - } - catch (SQLParseException exception) {} - } - - /** - * Tries to alter the rowid. - * - * @param driver The connection with Litebase. - */ - private void testInvalidRowidAlter(LitebaseConnection driver) - { - try - { - driver.executeUpdate("insert into employee (id, rowid, lastname) values (1, 2, 'imperial')"); - fail("129"); - } - catch (SQLParseException exception) {} - try - { - driver.executeUpdate("update employee set id = 1, rowid = 2, lastname = 'imperial'"); - fail("130"); - } - catch (SQLParseException exception) {} - } - - /** - * Tries to do a select * with too many fields. - * - * @param driver The connection with Litebase. - */ - private void testTooBigSelect(LitebaseConnection driver) - { - StringBuffer sBuffer = new StringBuffer(1201); - int i = 0; - - if (driver.exists("person1")) - driver.executeUpdate("drop table person1"); - sBuffer.append("create table person1 (a0 int"); - while (++i < 128) - sBuffer.append(", a").append(i).append(" int"); - sBuffer.append(")"); - driver.execute(sBuffer.toString()); - - sBuffer.setLength(0); - i = 0; - if (driver.exists("person2")) - driver.executeUpdate("drop table person2"); - sBuffer.append("create table person2 (a0 int"); - while (++i < 128) - sBuffer.append(", a").append(i).append(" int"); - sBuffer.append(")"); - driver.execute(sBuffer.toString()); - - sBuffer.setLength(0); - i = 0; - if (driver.exists("person3")) - driver.executeUpdate("drop table person3"); - sBuffer.append("create table person3 (a0 int"); - while (++i < 128) - sBuffer.append(", a").append(i).append(" int"); - sBuffer.append(")"); - driver.execute(sBuffer.toString()); - - try // Too many columns for a select. - { - driver.executeQuery("select * from person1, person2, person3"); - fail("131"); - } - catch (SQLParseException exception) {} - } - - /** - * Does tests with very long paths. - */ - private void testLongPath() - { - StringBuffer sBuffer = new StringBuffer(256); - String path; - int i; - - sBuffer.append(Convert.appendPath(Settings.appPath, "/")); - i = 256 - sBuffer.length(); - while (--i >= 0) - sBuffer.append('a'); - - - try // Path too long. - { - LitebaseConnection.getInstance("Test", sBuffer.toString()); - fail("132"); - } - catch (DriverException exception) {} - - // Path + table name too long. - sBuffer.setLength(245); - LitebaseConnection driver = LitebaseConnection.getInstance("Test", path = sBuffer.toString()); - try - { - driver.exists("person"); - fail("133"); - } - catch (DriverException exception) {} - try - { - driver.executeUpdate("drop table person"); - fail("134"); - } - catch (DriverException exception) {} - try - { - driver.execute("create table person (id int)"); - fail("135"); - } - catch (DriverException exception) {} - try - { - driver.executeQuery("select * from person"); - fail("136"); - } - catch (DriverException exception) {} - try - { - driver.recoverTable("person"); - fail("137"); - } - catch (DriverException exception) {} - try - { - driver.convert("person"); - fail("138"); - } - catch (DriverException exception) {} - driver.closeAll(); - - try - { - new File(path).delete(); - } - catch (IOException exception) - { - exception.printStackTrace(); - fail("139"); - } - - // File + table name too long for purge. - sBuffer.setLength(244); - driver = LitebaseConnection.getInstance("Test", path = sBuffer.toString()); - if (driver.exists("p")) - driver.executeUpdate("drop table p"); - driver.execute("create table p (id int)"); - driver.executeUpdate("insert into p values (0)"); - driver.executeUpdate("insert into p values (1)"); - driver.executeUpdate("delete p where id = 0"); - try - { - driver.purge("p"); - fail("140"); - } - catch (DriverException exception) {} - driver.executeUpdate("drop table p"); - driver.closeAll(); - - try - { - File file = new File(path + "/Test-p_.db"); - if (file.exists()) - file.delete(); - new File(path).delete(); - } - catch (IOException exception) - { - exception.printStackTrace(); - fail("141"); - } - } - - private void testInvalidParameter() - { - try - { - LitebaseConnection.getInstance("Test", "xpto; chars_type = ascii"); - fail("140"); - } - catch (DriverException exception) {} - try - { - LitebaseConnection.getInstance("Test", "crypto; xpto"); - fail("141"); - } - catch (DriverException exception) {} - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestJoin.java b/LitebaseSDK/src/java/samples/sys/testcases/TestJoin.java deleted file mode 100644 index 4ebb431bcf..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestJoin.java +++ /dev/null @@ -1,1369 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * This test verifies the joins on Litebase. - */ -public class TestJoin extends TestCase -{ - LitebaseConnection driver; - - /** - * The main method of the test. - */ - public void testRun() - { - driver = AllTests.getInstance("Test"); // First creates the connection with Litebase. - testJoinSintax(); // Tests the join sintax. - testSimpleJoins(); // Tests simple joins. - testComparisons(); // Tests comparison between fields, including null values and functions. - test3TablesJoin(); // Tests joins with 3 tables. - testRSColName(); // Tests ResultSet getting fields by column name. - testJoinWithIndex(); // Tests join with index. - testComparisonsWithIndices(); // Tests comparison between fields, using index. - testPrimaryKeyAndOrdering(); // Tests join with primary key, clause and table orders. - testComparisonInTheSameTable(); // Tests comparison of columns of the same table. - testOrWithFalseConstantComparison(); // Tests join with or and false comparisons with constants. - testOrderGroupBy(); // Tests join with order and group by. - driver.closeAll(); - } - - /** - * Drops an especific table if it exists - * - * @param name The table name. - */ - private void dropTableIfExist(String name) - { - if (driver.exists(name)) - driver.executeUpdate("drop table " + name); - } - - /** - * Runs Tests queries that have parser errors. - * - * @param sql The sql command with parser errors. - */ - private void runSelectWithParserError(String sql) - { - try - { - driver.executeQuery(sql); - fail(); - } - catch (SQLParseException exception) {} - - } - - /** - * Tests the join sintax. All sql commands with parser error must throw an exception. - */ - private void testJoinSintax() - { - // Drop tables. - dropTableIfExist("person"); - dropTableIfExist("course"); - - // Create tables. - driver.execute("create table person(person_id int primary key, name char(20) default 'Est� sem nome: �^~' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.execute("create table course (name char(20) primary key, person_id int, address char(20))"); - - runSelectWithParserError("select * from person p where person.person_id = 1"); // The alias must be used in the where clause. - runSelectWithParserError("select p.name, person.age from person p"); // The alias must be used if the table has it. - - // The alias must be used in the order by clause. - runSelectWithParserError("select max(t.person_id) as maxname from course t where t.name = '1' order by course.name "); - - // The alias must be used in the group by clause. - runSelectWithParserError("select max(t.person_id) as maxname from course t where t.name = '1' group by course.name "); - - // An alias can't be used in the where clause. - runSelectWithParserError("select max(t.person_id) as maxname, t.name as tn from course t where tn = '1' group by t.name " - + "having maxname = 'sem nome'"); - - runSelectWithParserError("select p.name, p.age from person p where p.xxxx = 1"); // Unknown column xxxx. - runSelectWithParserError("select name from person, course"); // Ambigous column name. - runSelectWithParserError("select name as v, person_id as v from person, course"); // Repeated alias. - runSelectWithParserError("SELECT * FROM person course, course"); // person can't use course as alias without giving an alias to course. - runSelectWithParserError("SELECT course.address FROM person course, course person"); // Course is an alias for person. - - // person is an alias for course. - runSelectWithParserError("SELECT * FROM person course, course person where person.address = '' and person.age = 9"); - - runSelectWithParserError("SELECT * FROM course, person order by year"); // order by field doesn't exist. - - // c.name is not in the select clause. - runSelectWithParserError("select max(c.person_id) as maxname, p.name, p.birth, c.address, p.person_id from course c, person p " - + "where c.name = '1' order by c.name"); - - try // Select sql without sintax errors. - { - runSelectWithParserError("SELECT distinct * FROM person course, course person where course.age = 10 and person.address is not null"); - fail(); - } - catch (AssertionFailedError error) {} - } - - /** - * Tests simple joins. - */ - private void testSimpleJoins() - { - // Drops tables. - driver.executeUpdate("drop table perSon"); - dropTableIfExist("peRson2"); - - // Creates tables. - driver.execute("create table person(name char(5), age int, years int)"); - driver.execute("create table person2(name2 char(5), age2 int)"); - - // Populates tables. - driver.executeUpdate("Insert into person values ('RLN', 20, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 20, 2)"); - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - - ResultSet rs = driver.executeQuery("select name, name2, age, age2 from person, person2 where 'RLN' = person.name order by name2"); - assertNotNull(rs); - assertEquals(2, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person, person2 where age2 = 40 and name = 'RLN'")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person, person2 where name = 'RLN' and person2.age2 = 40 ")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person, person2 where person.name = 'RLN' or person2.age2 = 40 ")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person, person2 where age2 = 40 or person.name = 'RLN'")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2 from person, person2 where person.name = person2.name2 and (person.name = 'RLN' or person2.name2 = 'RLN')")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - rs.close(); - - // Tests the boolean operator NOT. - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person, person2 where not age2 <> 40 or not person.name <> 'RLN'")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - // Tests the boolean operator NOT. - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person,person2 where not years = 1 or not age2 = 30 " - + "order by age2")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - // Tests comparison between fields. - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person2,person where name = person2.name2")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - rs.close(); - } - - /** - * Tests comparison between fields, including null values and functions. - */ - private void testComparisons() - { - // Drop tables. - driver.executeUpdate("drop table perSon"); - driver.executeUpdate("drop table perSon2"); - - // Create tables. - driver.execute("create table person(name char(5), age int, years int)"); - driver.execute("create table person2(name2 char(5), age2 int)"); - - // Creates the index. - driver.execute("create index idx on person(name)"); - - // Populates the tables. - driver.executeUpdate("Insert into person values ('RLN', 20, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 30, 2)"); - driver.executeUpdate("Insert INTO person(name) values ('IND')"); - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - driver.executeUpdate("Insert INTO person2 values ('rln', 40)"); - driver.executeUpdate("Insert INTO person2(name2) values ('IND2')"); - - ResultSet rs = driver.executeQuery("select name, name2, age, age2 from person2, person where lower(person.name) = person2.name2"); - assertNotNull(rs); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person2, person where person.age = person2.age2")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - rs.close(); - - driver.executeUpdate("update person2 p set p.age2 = 30 where name2 = 'rln'"); - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person2, person where person.age = person2.age2 or name = name2")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, name2, age, age2 from person2, person where person.age = person2.age2 and name = name2")); - assertEquals(0, rs.getRowCount()); - rs.close(); - - // Empties tables. - driver.executeUpdate("delete from person"); - driver.executeUpdate("delete from person2"); - - // Populates person. - driver.executeUpdate("Insert into person values ('RLN', 40, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 20, 2)"); - driver.executeUpdate("Insert INTO person(name) values ('IND')"); - driver.executeUpdate("Insert INTO person values ('RLN', 10, 4)"); - driver.executeUpdate("Insert INTO person values ('IND', 80, null)"); - driver.executeUpdate("Insert INTO person values ('IND', 100, 5)"); - - // Populates person2. - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - driver.executeUpdate("Insert INTO person2(name2) values ('IND2')"); - - assertNotNull(rs = driver.executeQuery("select * from person2, person")); - assertEquals(18, rs.getRowCount()); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person2, person where age is null and years is null and age2 is null")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("IND", rs.getString("name")); - assertEquals("IND2", rs.getString("name2")); - assertNull(rs.getString("years")); - assertNull(rs.getString("age")); - assertNull(rs.getString("age2")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select count(*) as tt from person, person2")); - assertTrue(rs.next()); - assertEquals(18, rs.getInt(1)); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select count(*) as tt from person, person2 where name = 'IND'")); - assertTrue(rs.next()); - assertEquals(9, rs.getInt("tt")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, count(*) as tt from person, person2 group by name")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("IND", rs.getString("name")); - assertEquals(9, rs.getInt("tt")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals(3, rs.getInt("tt")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals(6, rs.getInt("tt")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select name, count(*) as tt from person, person2 group by name having tt > 3")); - assertEquals(2, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("IND", rs.getString("name")); - assertEquals(9, rs.getInt("tt")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals(6, rs.getInt("tt")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select age2, lower(name) as tt, age, upper(name2) as rr from person, person2 where name = name2 " - + "order by age")); - assertEquals(2, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("rln", rs.getString("tt")); - assertEquals("RLN", rs.getString("rr")); - assertEquals(10, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertTrue(rs.next()); - assertEquals("rln", rs.getString("tt")); - assertEquals("RLN", rs.getString("rr")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - rs.close(); - } - - /** - * Tests joins with 3 tables. - */ - private void test3TablesJoin() - { - // Drops Tables. - driver.executeUpdate("drop table perSon"); - driver.executeUpdate("drop table perSon2"); - dropTableIfExist("peRson3"); - - // Creates tables. - driver.execute("create table person(name char(5), age int, years int)"); - driver.execute("create table person2(name2 char(5), age2 int)"); - driver.execute("create table person3(name3 char(5), age3 int, years3 int)"); - - // Creates indices. - driver.execute("create index idx on person(name)"); - driver.execute("create index idx on person2(age2)"); - driver.execute("create index idx on person3(years3)"); - - // Populates person. - driver.executeUpdate("Insert into person values ('RLN', 40, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 20, 2)"); - driver.executeUpdate("Insert INTO person(name) values ('IND')"); - driver.executeUpdate("Insert INTO person values ('RLN', 10, 4)"); - driver.executeUpdate("Insert INTO person values ('IND', 80, null)"); - driver.executeUpdate("Insert INTO person values ('IND', 100, 5)"); - - // Populates person 2. - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - driver.executeUpdate("Insert INTO person2(name2) values ('IND2')"); - - // Populates person 3. - driver.executeUpdate("INSERT into person3 values ('ZN', 60, 25)"); - driver.executeUpdate("Insert INTO person3 values ('JN', 25, 60)"); - driver.executeUpdate("INSERT into person3 values ('IND', 20, 1)"); - driver.executeUpdate("Insert INTO person3 values ('IND', 30, 2)"); - driver.executeUpdate("INSERT INTO person3(name3) values ('RLN')"); - driver.executeUpdate("Insert INTO person3(years3, name3) values (50,'RLN')"); - driver.executeUpdate("INSERT into person3(name3, years3) values ('ZN', 60)"); - driver.executeUpdate("INSERT INTO person3(name3, age3) values ('JN', 90)"); - driver.executeUpdate("INSERT into person3 values ('ZN', 30, 25)"); - driver.executeUpdate("INSERT INTO person3 values ('JN', 2, 60)"); - driver.executeUpdate("INSERT into person3 values ('RLN', 1, 100)"); - driver.executeUpdate("Insert INTO person3 values ('IOG', 40, 2)"); - driver.executeUpdate("Insert INTO person3(name3) values ('IOG')"); - driver.executeUpdate("Insert INTO person3(years3, name3) values (1,'RLN')"); - driver.executeUpdate("Insert into person3(name3, years3) values ('ZN', 50)"); - driver.executeUpdate("Insert INTO person3(name3, age3) values ('JN', 75)"); - - ResultSet rs = driver.executeQuery("select * from person, person2, person3"); - assertNotNull(rs); - assertEquals(288, rs.getRowCount()); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2, person3 where name = name2")); - assertEquals(32, rs.getRowCount()); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2, person3 where name = name2 and person3.age3 is null")); - assertEquals(12, rs.getRowCount()); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2, person3 where name = name2 and person3.age3 is null and name3 = 'IOG' " - + "and age = 10")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals("IOG", rs.getString("name3")); - assertEquals(10, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertNull(rs.getString("age3")); - assertEquals(4, rs.getInt("years")); - assertNull(rs.getString("years3")); - rs.close(); - } - - /** - * Tests ResultSet getting fields by column name. - */ - private void testRSColName() - { - // Drop tables. - driver.executeUpdate("drop table perSon"); - driver.executeUpdate("drop table perSon2"); - - // Create tables. - driver.execute("create table person(name char(5), age int, years int)"); - driver.execute("create table person2(name char(5), age2 int)"); - - // Populates table person. - driver.executeUpdate("Insert into person values ('RLN', 20, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 30, 2)"); - - // Populates table person2. - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - - ResultSet rs = driver.executeQuery("select * from person, person2"); - assertEquals(4, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString(1)); - assertEquals("20", rs.getString(2)); - assertEquals("1", rs.getString(3)); - assertEquals("RLN", rs.getString(4)); - assertEquals("30", rs.getString(5)); - assertEquals("RLN", rs.getString("person.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("20", rs.getString("person.age")); - assertEquals("20", rs.getString("age")); - assertEquals("1", rs.getString("person.years")); - assertEquals("1", rs.getString("years")); - assertEquals("RLN", rs.getString("person2.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("30", rs.getString("person2.age2")); - assertEquals("30", rs.getString("age2")); - - assertTrue(rs.next()); - assertEquals("RLN", rs.getString(1)); - assertEquals("20", rs.getString(2)); - assertEquals("1", rs.getString(3)); - assertEquals("IAPU", rs.getString(4)); - assertEquals("40", rs.getString(5)); - assertEquals("RLN", rs.getString("person.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("20", rs.getString("person.age")); - assertEquals("20", rs.getString("age")); - assertEquals("1", rs.getString("person.years")); - assertEquals("1", rs.getString("years")); - assertEquals("IAPU", rs.getString("person2.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("40", rs.getString("person2.age2")); - assertEquals("40", rs.getString("age2")); - - assertTrue(rs.next()); - assertEquals("IOG", rs.getString(1)); - assertEquals("30", rs.getString(2)); - assertEquals("2", rs.getString(3)); - assertEquals("RLN", rs.getString(4)); - assertEquals("30", rs.getString(5)); - assertEquals("IOG", rs.getString("person.name")); - assertEquals("IOG", rs.getString("name")); - assertEquals("30", rs.getString("person.age")); - assertEquals("30", rs.getString("age")); - assertEquals("2", rs.getString("person.years")); - assertEquals("2", rs.getString("years")); - assertEquals("RLN", rs.getString("person2.name")); - assertEquals("IOG", rs.getString("name")); - assertEquals("30", rs.getString("person2.age2")); - assertEquals("30", rs.getString("age2")); - - assertTrue(rs.next()); - assertEquals("IOG", rs.getString(1)); - assertEquals("30", rs.getString(2)); - assertEquals("2", rs.getString(3)); - assertEquals("IAPU", rs.getString(4)); - assertEquals("40", rs.getString(5)); - assertEquals("IOG", rs.getString("person.name")); - assertEquals("IOG", rs.getString("name")); - assertEquals("30", rs.getString("person.age")); - assertEquals("30", rs.getString("age")); - assertEquals("2", rs.getString("person.years")); - assertEquals("2", rs.getString("years")); - assertEquals("IAPU", rs.getString("person2.name")); - assertEquals("IOG", rs.getString("name")); - assertEquals("40", rs.getString("person2.age2")); - assertEquals("40", rs.getString("age2")); - rs.close(); - - assertEquals(4, (rs = driver.executeQuery("select person.name as tt, age, years as bb, person2.name, person2.age2 from person, person2")) - .getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString(1)); - assertEquals("20", rs.getString(2)); - assertEquals("1", rs.getString(3)); - assertEquals("RLN", rs.getString(4)); - assertEquals("30", rs.getString(5)); - assertEquals("RLN", rs.getString("person.tt")); - assertEquals("RLN", rs.getString("tt")); - assertEquals("20", rs.getString("person.age")); - assertEquals("20", rs.getString("age")); - assertEquals("1", rs.getString("person.bb")); - assertEquals("1", rs.getString("bb")); - assertEquals("RLN", rs.getString("person2.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("30", rs.getString("person2.age2")); - assertEquals("30", rs.getString("age2")); - - assertTrue(rs.next()); - assertEquals("RLN", rs.getString(1)); - assertEquals("20", rs.getString(2)); - assertEquals("1", rs.getString(3)); - assertEquals("IAPU", rs.getString(4)); - assertEquals("40", rs.getString(5)); - assertEquals("RLN", rs.getString("person.tt")); - assertEquals("RLN", rs.getString("tt")); - assertEquals("20", rs.getString("person.age")); - assertEquals("20", rs.getString("age")); - assertEquals("1", rs.getString("person.bb")); - assertEquals("1", rs.getString("bb")); - assertEquals("IAPU", rs.getString("person2.name")); - assertEquals("IAPU", rs.getString("name")); - assertEquals("40", rs.getString("person2.age2")); - assertEquals("40", rs.getString("age2")); - - assertTrue(rs.next()); - assertEquals("IOG", rs.getString(1)); - assertEquals("30", rs.getString(2)); - assertEquals("2", rs.getString(3)); - assertEquals("RLN", rs.getString(4)); - assertEquals("30", rs.getString(5)); - assertEquals("IOG", rs.getString("person.tt")); - assertEquals("IOG", rs.getString("tt")); - assertEquals("30", rs.getString("person.age")); - assertEquals("30", rs.getString("age")); - assertEquals("2", rs.getString("person.bb")); - assertEquals("2", rs.getString("bb")); - assertEquals("RLN", rs.getString("person2.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("30", rs.getString("person2.age2")); - assertEquals("30", rs.getString("age2")); - - assertTrue(rs.next()); - assertEquals("IOG", rs.getString(1)); - assertEquals("30", rs.getString(2)); - assertEquals("2", rs.getString(3)); - assertEquals("IAPU", rs.getString(4)); - assertEquals("40", rs.getString(5)); - assertEquals("IOG", rs.getString("person.tt")); - assertEquals("IOG", rs.getString("tt")); - assertEquals("30", rs.getString("person.age")); - assertEquals("30", rs.getString("age")); - assertEquals("2", rs.getString("person.bb")); - assertEquals("2", rs.getString("bb")); - assertEquals("IAPU", rs.getString("person2.name")); - assertEquals("IAPU", rs.getString("name")); - assertEquals("40", rs.getString("person2.age2")); - assertEquals("40", rs.getString("age2")); - rs.close(); - - assertEquals((rs = driver.executeQuery("select person.name, person2.name from person, person2")).getRowCount(),4); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString(1)); - assertEquals("RLN", rs.getString(2)); - assertEquals("RLN", rs.getString("person.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("person2.name")); - assertEquals("RLN", rs.getString("name")); - - assertTrue(rs.next()); - assertEquals("RLN", rs.getString(1)); - assertEquals("IAPU", rs.getString(2)); - assertEquals("RLN", rs.getString("person.name")); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("person2.name")); - assertEquals("RLN", rs.getString("name")); - - assertTrue(rs.next()); - assertEquals("IOG", rs.getString(1)); - assertEquals("RLN", rs.getString(2)); - assertEquals("IOG", rs.getString("person.name")); - assertEquals("IOG", rs.getString("name")); - assertEquals("RLN", rs.getString("person2.name")); - assertEquals("IOG", rs.getString("name")); - - assertTrue(rs.next()); - assertEquals("IOG", rs.getString(1)); - assertEquals("IAPU", rs.getString(2)); - assertEquals("IOG", rs.getString("person.name")); - assertEquals("IOG", rs.getString("name")); - assertEquals("IAPU", rs.getString("person2.name")); - assertEquals("IOG", rs.getString("name")); - rs.close(); - } - - /** - * Tests join with index. - */ - private void testJoinWithIndex() - { - // Drops tables. - driver.executeUpdate("drop table perSon"); - driver.executeUpdate("drop table perSon2"); - - // Creates tables. - driver.execute("create table person(name char(5), age int, years int)"); - driver.execute("create table person2(name2 char(5), age2 int)"); - - driver.execute("create index idx on person(name)"); // Creates index for column person.name. - - testIndexOnAge(); // Tests without index on person.age. - driver.execute("create index idx on person(age)"); // Creates the index. - testIndexOnAge(); // Tests with index on person.age. - - testIndexOnName2(); // Tests without index on person2.name, - driver.execute("create index idx on person2(name2)"); // Creates the index. - testIndexOnName2(); // Tests with index on pers - } - - /** - * Tests joins with and without index on person.age. - */ - private void testIndexOnAge() - { - // Empties tables. - driver.executeUpdate("delete from person"); - driver.executeUpdate("delete from person2"); - - // Populates table person. - driver.executeUpdate("Insert into person values ('RLN', 20, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 30, 2)"); - driver.executeUpdate("Insert into person values ('RLN2', 40, 3)"); - - // Populates table person2. - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - driver.executeUpdate("Insert INTO person2 values ('rln', 30)"); - - ResultSet rs = driver.executeQuery("select * from person,person2 where name = 'RLN'"); - assertNotNull(rs); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - rs.close(); - - driver.executeUpdate("update person set name = 'RLN' where age = 40"); - assertNotNull(rs = driver.executeQuery("select * from person,person2 where name = 'RLN' and age = 40")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - driver.executeUpdate("update person set name = 'RLN2' where age = 40"); - assertNotNull(rs = driver.executeQuery("select * from person,person2 where name = 'RLN' or age = 40")); - assertEquals(6, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person,person2 where name = 'RLN2' and age = 40")); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - } - - /** - * Tests joins with and without index on person2.name2. - */ - private void testIndexOnName2() - { - ResultSet rs = driver.executeQuery("select * from person, person2 where name = 'RLN' and name2 = 'rln'"); - assertNotNull(rs); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name2 = 'rln' and name = 'RLN'")); - assertEquals(rs.getRowCount(),1); - assertTrue(rs.next()); - assertEquals(rs.getString("name"),"RLN"); - assertEquals(rs.getString("name2"),"rln"); - assertEquals(rs.getInt("age"),20); - assertEquals(rs.getInt("age2"),30); - assertEquals(rs.getInt("years"),1); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name = 'RLN' or name2 = 'rln'")); - assertEquals(5, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(2, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name2 = 'rln' or name = 'RLN'")); - assertEquals(5, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(2, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where (name = 'RLN' or name2 = 'rln') or name = 'RLN2'")); - assertEquals(7, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(2, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("IAPU", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(40, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where (name = 'RLN' or name2 = 'rln') and name = 'RLN2'")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name = 'RLN2' and (name = 'RLN' or name2 = 'rln')" )); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN2", rs.getString("name")); - assertEquals("rln", rs.getString("name2")); - assertEquals(40, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(3, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name = name2")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - rs.close(); - } - - /** - * Tests comparison between fields, using index. - */ - private void testComparisonsWithIndices() - { - // Drops tables. - driver.executeUpdate("drop table perSon"); - driver.executeUpdate("drop table perSon2"); - - // Creates tables. - driver.execute("create table peRson(name char(5), age int, years int)"); - driver.execute("create table pErson2(name2 char(5), age2 int)"); - - // Populates person. - driver.executeUpdate("Insert into person values ('RLN', 20, 1)"); - driver.executeUpdate("Insert INTO person values ('IOG', 30, 2)"); - driver.executeUpdate("Insert into person values ('RLN2', 40, 3)"); - - // Populates person2. - driver.executeUpdate("Insert into person2 values ('RLN', 30)"); - driver.executeUpdate("Insert INTO person2 values ('IAPU', 40)"); - driver.executeUpdate("Insert INTO person2 values ('rln', 30)"); - driver.executeUpdate("Insert INTO person2 values ('RLN', 60)"); - driver.executeUpdate("Insert INTO person2 values ('IOG', 70)"); - - // Creates the indices. - driver.execute("create index idx on person(name)"); - driver.execute("create index idx on person2(name2)"); - driver.execute("create index idx on person(age)"); - driver.execute("create index idx on person2(age2)"); - - ResultSet rs = driver.executeQuery("select * from person, person2 where name = name2"); - assertNotNull(rs); - assertEquals(3, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(60, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IOG", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(70, rs.getInt("age2")); - assertEquals(2, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name = name2 and name = 'IOG'")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("IOG", rs.getString("name")); - assertEquals("IOG", rs.getString("name2")); - assertEquals(30, rs.getInt("age")); - assertEquals(70, rs.getInt("age2")); - assertEquals(2, rs.getInt("years")); - rs.close(); - - driver.executeUpdate("update person2 set age2 = 20 where age2 = 60"); - assertNotNull(rs = driver.executeQuery("select * from person, person2 where name = name2 and age = age2")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(20, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - rs.close(); - - assertNotNull(rs = driver.executeQuery("select * from person, person2 where age = age2 and name = name2")); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals(20, rs.getInt("age")); - assertEquals(20, rs.getInt("age2")); - assertEquals(1, rs.getInt("years")); - rs.close(); - - // Creates and populates person3. - driver.executeUpdate("drop table perSon3"); - driver.execute("create table person3(name3 char(5), age3 int, years3 int)"); - driver.executeUpdate("INSERT into person3 values ('ZN', 60, 25)"); - driver.executeUpdate("Insert INTO person3 values ('JN', 25, 60)"); - driver.executeUpdate("INSERT into person3 values ('IND', 20, 1)"); - driver.executeUpdate("INSERT into person3 values ('RLN', 1, 100)"); - driver.executeUpdate("Insert INTO person3 values ('IND', 30, 2)"); - driver.executeUpdate("Insert INTO person3 values ('RLN2', 30, 2)"); - driver.execute("create index idx on person3(name3)"); - - // Tests the same join bu with different table ordering. - testTableOrder("select * from person, person2, person3 where name = name2 and name = name3"); - testTableOrder("select * from person, person3, person2 where name = name2 and name = name3"); - testTableOrder("select * from person2, person3, person where name = name2 and name = name3"); - testTableOrder("select * from person2, person, person3 where name = name2 and name = name3"); - testTableOrder("select * from person3, person2, person where name = name2 and name = name3"); - testTableOrder("select * from person3, person, person2 where name = name2 and name = name3"); - } - - /** - * Tests the same join bu with different table ordering. - * - * @param sql The sql select command of the join. - */ - private void testTableOrder(String sql) - { - ResultSet rs = driver.executeQuery(sql); - assertNotNull(rs); - assertEquals(2, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals("RLN", rs.getString("name3")); - assertEquals(20, rs.getInt("age")); - assertEquals(30, rs.getInt("age2")); - assertEquals(1, rs.getInt("age3")); - assertEquals(1, rs.getInt("years")); - assertEquals(100, rs.getInt("years3")); - assertTrue(rs.next()); - assertEquals("RLN", rs.getString("name")); - assertEquals("RLN", rs.getString("name2")); - assertEquals("RLN", rs.getString("name3")); - assertEquals(20, rs.getInt("age")); - assertEquals(20, rs.getInt("age2")); - assertEquals(1, rs.getInt("age3")); - assertEquals(1, rs.getInt("years")); - assertEquals(100, rs.getInt("years3")); - rs.close(); - } - - /** - * Tests join with primary key, clause and table orders. - */ - private void testPrimaryKeyAndOrdering() - { - // Drop tables. - driver.executeUpdate("drop table perSon"); - driver.executeUpdate("drop table Course"); - - // Creates tables. - driver.execute("create table person (id long primary key, lastname char(20), firstname char(20))"); - driver.execute("create table course (id long primary key, personid long, cname char(20))"); - - // Populates the tables - PreparedStatement ptInsert = driver.prepareStatement("insert into person (id,lastname,firstname) values (?,?,?)"); - PreparedStatement acctInsert = driver.prepareStatement("insert into course (id,personid,cname) values (?,?,?)"); - int i = 101; - while (--i > 0) - { - ptInsert.setLong(0, i); - ptInsert.setString(1, "Last" + i); - ptInsert.setString(2, "First" + i); - ptInsert.executeUpdate(); - - acctInsert.setLong(0, i + 100); - acctInsert.setLong(1, i); - acctInsert.setString(2, "Course" + i); - acctInsert.executeUpdate(); - } - - // There is only one person with id 50. - ResultSet rs = driver.executeQuery("select person.id from person where person.id = 50"); - assertEquals(1, rs.getRowCount()); - rs.close(); - - // All the joins must return only one record because only one person has id 50. - assertEquals(1, (rs = driver.executeQuery("select course.id from course where course.personid = 50")).getRowCount()); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from person,course where person.id = course.personid and person.id = 50")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from course, person where person.id = course.personid and person.id = 50")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from person,course where course.personid = person.id and person.id = 50")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from course, person where course.personid = person.id and person.id = 50")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from person,course where person.id = 50 and course.personid = person.id")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from course, person where person.id = 50 and course.personid = person.id")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from person,course where person.id = 50 and course.personid = person.id")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select person.id, course.id from course, person where person.id = 50 and course.personid = person.id")).getRowCount()); - rs.next(); - assertEquals(50, rs.getLong(1)); - assertEquals(150, rs.getLong(2)); - rs.close(); - } - - /** - * Tests comparison of columns of the same table. - */ - void testComparisonInTheSameTable() - { - LitebaseConnection driverAux = driver; - - if (driverAux.exists("M_META_GRUPO_MATERIAL")) - driverAux.executeUpdate("drop table M_META_GRUPO_MATERIAL"); - driverAux.execute("create table M_META_GRUPO_MATERIAL (GMF_CODIGO long NOT NULL, QTD_META double, QTD_CONSUMIDA double)"); - driverAux.executeUpdate("insert into M_META_GRUPO_MATERIAL values (1, 500.6578844, 0)"); - driverAux.executeUpdate("insert into M_META_GRUPO_MATERIAL values (1, 350.50, 350.50)"); - - ResultSet resultSet = driver.executeQuery("select * from M_META_GRUPO_MATERIAL MGM where MGM.QTD_CONSUMIDA = MGM.QTD_META"); - assertEquals(1, resultSet.getRowCount()); - resultSet.close(); - - if (driverAux.exists("M_MATERIAL")) - driverAux.executeUpdate("drop table M_MATERIAL"); - driverAux.execute("create table M_MATERIAL (CODIGO_MATERIAL int NOT NULL, GMF_CODIGO long, primary key(CODIGO_MATERIAL))"); - - driverAux.executeUpdate("insert into M_MATERIAL values (1, 1)"); - driverAux.executeUpdate("insert into M_MATERIAL values (2, 1)"); - - assertEquals(2, (resultSet = driverAux.executeQuery("select * from M_MATERIAL MM, M_META_GRUPO_MATERIAL MGM where MM.GMF_CODIGO=MGM.GMF_CODIGO and MM.CODIGO_MATERIAL=1")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driverAux.executeQuery("select * from M_MATERIAL MM, M_META_GRUPO_MATERIAL MGM where MM.GMF_CODIGO=MGM.GMF_CODIGO and MM.CODIGO_MATERIAL=1 and MGM.QTD_CONSUMIDA = MGM.QTD_META")).getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driverAux.executeQuery("select * from M_MATERIAL MM, M_META_GRUPO_MATERIAL MGM where MM.GMF_CODIGO=MGM.GMF_CODIGO and MM.CODIGO_MATERIAL=1 and MGM.QTD_CONSUMIDA = 350.5")).getRowCount()); - resultSet.close(); - } - - /** - * Tests join with or and false comparisons with constants. - */ - private void testOrWithFalseConstantComparison() - { - LitebaseConnection driverAux = driver; - - // Drops existing tables. - if (driverAux.exists("tbpedido")) - driverAux.executeUpdate("drop table tbpedido"); - if (driverAux.exists("tbcliente")) - driverAux.executeUpdate("drop table tbcliente"); - - // Creates the tables. - driverAux.execute("create table tbpedido (cdpedido short, totalpedido double, cdcliente short)"); - driverAux.execute("create table tbcliente (cdcliente short, nmcliente char(10), flativo char(1))"); - - // Populates the tables. - driverAux.executeUpdate("insert into tbpedido values (1, 11.30, 1)"); - driverAux.executeUpdate("insert into tbpedido values (2, 19.30, 1)"); - driverAux.executeUpdate("insert into tbpedido values (3, 17.20, 2)"); - driverAux.executeUpdate("insert into tbpedido values (4, 50.00, 2)"); - driverAux.executeUpdate("insert into tbpedido values (5, 45.70, 3)"); - driverAux.executeUpdate("insert into tbpedido values (6, 78.00, 4)"); - driverAux.executeUpdate("insert into tbpedido values (7, 79.10, 5)"); - driverAux.executeUpdate("insert into tbpedido values (8, 80.00, 3)"); - driverAux.executeUpdate("insert into tbpedido values (9, 2.50, 4)"); - - driverAux.executeUpdate("insert into tbcliente values (1, 'Jos�', 'S')"); - driverAux.executeUpdate("insert into tbcliente values (2, 'Maria', 'S')"); - driverAux.executeUpdate("insert into tbcliente values (3, 'Jo�o', 'S')"); - driverAux.executeUpdate("insert into tbcliente values (4, 'Antonio', 'S')"); - driverAux.executeUpdate("insert into tbcliente values (5, 'Lucia', 'S')"); - - // Normal select. - ResultSet resultSet - = driverAux.executeQuery("select * from tbpedido ped, tbcliente cli where cli.cdcliente = ped.cdcliente and (ped.cdcliente = 1)"); - assertEquals(2, resultSet.getRowCount()); - resultSet.close(); - - // Selects with false constant comparisons. - assertEquals(2, (resultSet = driverAux.executeQuery("select * from tbpedido ped, tbcliente cli where cli.cdcliente = ped.cdcliente and " - + "(ped.cdcliente = 1 or '1' = '0')")).getRowCount()); - resultSet.close(); - - assertEquals(2, (resultSet = driverAux.executeQuery("select * from tbpedido ped, tbcliente cli where cli.cdcliente = ped.cdcliente and " - + "('1' = '0' or ped.cdcliente = 1)")).getRowCount()); - resultSet.close(); - - assertEquals(2, (resultSet = driverAux.executeQuery("select * from tbpedido ped, tbcliente cli where (ped.cdcliente = 1 or '1' = '0') and " - + "cli.cdcliente = ped.cdcliente")).getRowCount()); - resultSet.close(); - - assertEquals(2, (resultSet = driverAux.executeQuery("select * from tbpedido ped, tbcliente cli where ('1' = '0' or ped.cdcliente = 1) and " - + "cli.cdcliente = ped.cdcliente")).getRowCount()); - resultSet.close(); - } - - /** - * Tests join with order and group by. - */ - void testOrderGroupBy() - { - LitebaseConnection driverAux = driver; - - // Drops existing tables. - if (driverAux.exists("PERGUNTA")) - driverAux.executeUpdate("drop table PERGUNTA"); - if (driverAux.exists("ASSGRUPOPERGUNTA")) - driverAux.executeUpdate("drop table ASSGRUPOPERGUNTA"); - if (driverAux.exists("ASSGRPPERGTITULOCHK")) - driverAux.executeUpdate("drop table ASSGRPPERGTITULOCHK"); - - // Creates the tables. - driverAux.execute("CREATE TABLE PERGUNTA (IDPERGUNTA LONG PRIMARY KEY, DESCRICAO VARCHAR(115) NOT NULL, SITUACAO INT NOT NULL, " - + "FOTO INT NOT NULL)"); - driverAux.execute("CREATE TABLE ASSGRUPOPERGUNTA(IDASSGRUPOPERGUNTA LONG PRIMARY KEY, IDPERGUNTA LONG NOT NULL, " + - "IDGRUPOPERGUNTA LONG NOT NULL, ORDEM INT NOT NULL, SITUACAO INT NOT NULL)"); - driverAux.execute("CREATE TABLE ASSGRPPERGTITULOCHK(IDASSGRPPERGTITULOCHK LONG PRIMARY KEY, IDFORMULARIO LONG NOT NULL, " - + "IDASSGRUPOPERGUNTA LONG NOT NULL, SITUACAO INT NOT NULL)"); - - // Populates the tables. - driverAux.executeUpdate("insert into pergunta values (57, 'Desligar todas as fontes de tensao', 1, 0)"); - driverAux.executeUpdate("insert into ASSGRUPOPERGUNTA values (262, 57, 14, 1, 1)"); - driverAux.executeUpdate("insert into ASSGRPPERGTITULOCHK values (441, 4, 262, 1)"); - - // The joins. - ResultSet resultSet = driverAux.executeQuery("select ASS.IDASSGRUPOPERGUNTA, P.IDPERGUNTA, P.DESCRICAO, P.FOTO from PERGUNTA P, " -+ "ASSGRUPOPERGUNTA ASS, ASSGRPPERGTITULOCHK ASS1 where ASS.IDGRUPOPERGUNTA=14 AND ASS1.IDFORMULARIO = 4 AND ASS.SITUACAO = 1 AND ASS1.SITUACAO = 1 " -+ "AND P.SITUACAO = 1 AND ASS.IDPERGUNTA = P.IDPERGUNTA AND ASS1.IDASSGRUPOPERGUNTA = ASS.IDASSGRUPOPERGUNTA order by ASS.ORDEM"); - assertEquals(1, resultSet.getRowCount()); - resultSet.close(); - - assertEquals(1, (resultSet = driverAux.executeQuery("select ASS.IDASSGRUPOPERGUNTA, P.IDPERGUNTA, P.DESCRICAO, P.FOTO from PERGUNTA P, " -+ "ASSGRUPOPERGUNTA ASS, ASSGRPPERGTITULOCHK ASS1 where ASS.IDGRUPOPERGUNTA=14 AND ASS1.IDFORMULARIO = 4 AND ASS.SITUACAO = 1 AND ASS1.SITUACAO = 1 " -+ "AND P.SITUACAO = 1 AND ASS.IDPERGUNTA = P.IDPERGUNTA AND ASS1.IDASSGRUPOPERGUNTA = ASS.IDASSGRUPOPERGUNTA group by ASS.IDASSGRUPOPERGUNTA, P.IDPERGUNTA, P.DESCRICAO, P.FOTO")).getRowCount()); - resultSet.close(); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestLogger.java b/LitebaseSDK/src/java/samples/sys/testcases/TestLogger.java deleted file mode 100644 index 169af72574..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestLogger.java +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.LitebaseConnection; -import totalcross.sys.Vm; -import totalcross.unit.TestCase; - -/** - * Tests the oppening and deletion of Litebase logger files. - */ -public class TestLogger extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - // Turn logging off and erases all log files. - if (AllTests.useLogger) - { - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - } - LitebaseConnection.deleteLogFiles(); - - assertEquals(0, LitebaseConnection.deleteLogFiles()); // No logger files to be deleted. - - // Creates one logger file and tries to delete it when using and after using it. - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - assertEquals(0, LitebaseConnection.deleteLogFiles()); - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - assertEquals(1, LitebaseConnection.deleteLogFiles()); - - // Creates two logger files. Just the not currently used one can be deleted. - int time = Vm.getTimeStamp(); - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - while (Vm.getTimeStamp() - time < 2000) { - Thread.yield(); - } - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - assertEquals(1, LitebaseConnection.deleteLogFiles()); - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - assertEquals(1, LitebaseConnection.deleteLogFiles()); - - // Creates three logger files. Just the not currently used ones can be deleted. - time = Vm.getTimeStamp(); - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - while (Vm.getTimeStamp() - time < 1000) { - Thread.yield(); - } - time = Vm.getTimeStamp(); - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - while (Vm.getTimeStamp() - time < 1000) { - Thread.yield(); - } - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - assertEquals(2, LitebaseConnection.deleteLogFiles()); - LitebaseConnection.logger.dispose(true); - LitebaseConnection.logger = null; - assertEquals(1, LitebaseConnection.deleteLogFiles()); - - if (AllTests.useLogger) // If the tests are using logger, turn it on again. - LitebaseConnection.logger = LitebaseConnection.getDefaultLogger(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestMaxMin.java b/LitebaseSDK/src/java/samples/sys/testcases/TestMaxMin.java deleted file mode 100644 index c859bfbc3b..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestMaxMin.java +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.Time; -import totalcross.unit.TestCase; - -/** - * Tests max and min aggregation functions with and without indices. - */ -public class TestMaxMin extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection connection = LitebaseConnection.getInstance("Test"); - - if (connection.exists("person")) - connection.executeUpdate("drop table person"); - connection.execute("create table person (name char(10), cpf char(10), birth datetime, id long)"); - PreparedStatement ps = connection.prepareStatement("insert into person values (?, ?, ?, ?)"); - - testEmptyTable(connection); - - // Table without repetitions. - int i = 2000; - Time time = new Time(2011, 7, 19, 0, 0, 0, 0); - - connection.setRowInc("person", 2000); - while (--i >= 0) - { - ps.setString(0, "name" + i); - ps.setString(1, "cpf" + (1999 - i)); - ps.setDateTime(2, time); - ps.setLong(3, i); - ps.executeUpdate(); - time.inc(0, 0, 1); - } - connection.setRowInc("person", -1); - executeAllTests(connection); - - // Table with repetitions. - i = 2000; - time.inc(0, 0, -2000); - connection.setRowInc("person", 2000); - while (--i >= 0) - { - ps.setString(0, "name" + i); - ps.setString(1, "cpf" + (1999 - i)); - ps.setLong(3, i); - ps.setDateTime(2, time); - ps.executeUpdate(); - ps.setDateTime(2, (Time)null); - ps.executeUpdate(); - time.inc(0, 0, 1); - } - connection.setRowInc("person", -1); - executeAllTests(connection); - connection.closeAll(); - } - - /** - * Test with empty or all rows deleted. - * - * @param connection The connection with Litebase. - */ - private void testEmptyMaxMin(LitebaseConnection connection) - { - ResultSet resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc from person"); - assertEquals(0, resultSet.getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name > 'name0' and cpf > 'cpf0'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name > 'name0' and cpf < 'cpf999'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name < 'name999' and cpf > 'cpf0'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name0' and cpf = 'cpf1999'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name1999' and cpf = 'cpf0'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select count(*) as c, avg(rowid) as a, max(name) as maxn, min(name) as minn, " - + "max(cpf) as maxc, min(cpf) as minc from person")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests datetime. - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth is null")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth is not null")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth > '2011/07/19'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth < '2011/07/20'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth like '2011/07/19%'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth like '2011/07/18%'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(id) as maxi, min(id) as mini from person")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Does all tests with empty or all rows deleted. - * - * @param connection The connection with Litebase. - */ - private void testEmptyTable(LitebaseConnection connection) - { - testEmptyMaxMin(connection); - connection.execute("create index idx on person (name)"); - testEmptyMaxMin(connection); - connection.execute("create index idx on person (cpf)"); - testEmptyMaxMin(connection); - connection.execute("create index idx on person (birth)"); - testEmptyMaxMin(connection); - connection.executeUpdate("drop index * on person"); - testEmptyMaxMin(connection); - connection.execute("create index idx on person (name, cpf)"); - testEmptyMaxMin(connection); - connection.execute("create index idx on person (cpf, name)"); - testEmptyMaxMin(connection); - connection.execute("create index idx on person (birth, rowid)"); - testEmptyMaxMin(connection); - connection.execute("create index idx on person (id)"); - testEmptyMaxMin(connection); - connection.executeUpdate("drop index * on person"); - testEmptyMaxMin(connection); - } - - /** - * Tests max or min with a populated table. - * - * @param connection The connection with Litebase. - */ - private void testMaxMin(LitebaseConnection connection) - { - ResultSet resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc from person"); - assertEquals(1, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name999"); - assertEquals(resultSet.getString("minn"), "name0"); - assertEquals(resultSet.getString("maxc"), "cpf999"); - assertEquals(resultSet.getString("minc"), "cpf0"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name > 'name0' and cpf > 'cpf0'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name999"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf999"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name > 'name0' and cpf < 'cpf999'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name999"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf998"); - assertEquals(resultSet.getString("minc"), "cpf0"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name < 'name999' and cpf > 'cpf0'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name998"); - assertEquals(resultSet.getString("minn"), "name0"); - assertEquals(resultSet.getString("maxc"), "cpf999"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name0' and cpf = 'cpf1999'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name0"); - assertEquals(resultSet.getString("minn"), "name0"); - assertEquals(resultSet.getString("maxc"), "cpf1999"); - assertEquals(resultSet.getString("minc"), "cpf1999"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name1999' and cpf = 'cpf0'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name1999"); - assertEquals(resultSet.getString("minn"), "name1999"); - assertEquals(resultSet.getString("maxc"), "cpf0"); - assertEquals(resultSet.getString("minc"), "cpf0"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select count(*) as c, avg(rowid) as a, max(name) as maxn, min(name) as minn, " - + "max(cpf) as maxc, min(cpf) as minc from person")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name999"); - assertEquals(resultSet.getString("minn"), "name0"); - assertEquals(resultSet.getString("maxc"), "cpf999"); - assertEquals(resultSet.getString("minc"), "cpf0"); - assertFalse(resultSet.next()); - resultSet.close(); - - // Empty tables should not return rows. - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests datetime. - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:19") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:00") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertLower((resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth is null")).getRowCount(), 2); - if (resultSet.next()) - { - assertNull(resultSet.getDateTime(1)); - assertNull(resultSet.getDateTime(2)); - } - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth is not null")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:19") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:00") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth > '2011/07/19'")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:19") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:01") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth < '2011/07/20'")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:19") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:00") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth like '2011/07/19%'")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:19") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:00") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth like '2011/07/18%'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(id) as maxi, min(id) as mini from person")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(1999, resultSet.getLong(1)); - assertEquals(0, resultSet.getLong(2)); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Tests max or min with some deletes. - * - * @param connection The connection with Litebase. - */ - private void testMaxMinWithDelete(LitebaseConnection connection) - { - ResultSet resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc from person"); - assertEquals(1, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name998"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf998"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name > 'name0' and cpf > 'cpf0'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name998"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf998"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name > 'name0' and cpf < 'cpf999'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name998"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf998"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name < 'name999' and cpf > 'cpf0'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name998"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf998"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name1' and cpf = 'cpf1998'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name1"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf1998"); - assertEquals(resultSet.getString("minc"), "cpf1998"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name1998' and cpf = 'cpf1'")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name1998"); - assertEquals(resultSet.getString("minn"), "name1998"); - assertEquals(resultSet.getString("maxc"), "cpf1"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select count(*) as c, avg(rowid) as a, max(name) as maxn, min(name) as minn, " - + "max(cpf) as maxc, min(cpf) as minc from person")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(resultSet.getString("maxn"), "name998"); - assertEquals(resultSet.getString("minn"), "name1"); - assertEquals(resultSet.getString("maxc"), "cpf998"); - assertEquals(resultSet.getString("minc"), "cpf1"); - assertFalse(resultSet.next()); - resultSet.close(); - - // Empty tables should not return rows. - assertEquals(0, (resultSet = connection.executeQuery("select max(name) as maxn, min(name) as minn, max(cpf) as maxc, min(cpf) as minc " - + "from person where name = 'name999' or cpf = 'cpf0'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests datetime. - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:18") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:01") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertLower((resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth is null")).getRowCount(), 2); - if (resultSet.next()) - { - assertNull(resultSet.getDateTime(1)); - assertNull(resultSet.getDateTime(2)); - } - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth is not null")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:18") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:01") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth > '2011/07/19'")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:18") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:01") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth < '2011/07/20'")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:18") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:01") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth like '2011/07/19%'")).getRowCount()); - assertTrue(resultSet.next()); - assertTrue(resultSet.getDateTime(1).toString().indexOf(":33:18") >= 0); - assertTrue(resultSet.getDateTime(2).toString().indexOf(":00:01") >= 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select max(birth) as maxb, min(birth) as mimb from person where birth like '2011/07/18%'")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(1, (resultSet = connection.executeQuery("select max(id) as maxi, min(id) as mini from person")).getRowCount()); - assertTrue(resultSet.next()); - assertEquals(1998, resultSet.getLong(1)); - assertEquals(1, resultSet.getLong(2)); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Execute all tests for the created tables. - * - * @param connection The connection with Litebase. - */ - private void executeAllTests(LitebaseConnection connection) - { - testMaxMin(connection); - connection.execute("create index idx on person (name)"); - testMaxMin(connection); - connection.execute("create index idx on person (cpf)"); - testMaxMin(connection); - connection.execute("create index idx on person (birth)"); - testMaxMin(connection); - connection.executeUpdate("drop index * on person"); - testMaxMin(connection); - connection.execute("create index idx on person (name, cpf)"); - testMaxMin(connection); - connection.execute("create index idx on person (cpf, name)"); - testMaxMin(connection); - connection.execute("create index idx on person (birth, rowid)"); - testMaxMin(connection); - connection.execute("create index idx on person (id)"); - testMaxMin(connection); - connection.executeUpdate("drop index * on person"); - testMaxMin(connection); - - connection.executeUpdate("delete from person where name = 'name0' or cpf = 'cpf0' or name = 'name999' or cpf = 'cpf999'"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (name)"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (cpf)"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (birth)"); - testMaxMinWithDelete(connection); - connection.executeUpdate("drop index * on person"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (name, cpf)"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (cpf, name)"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (birth, rowid)"); - testMaxMinWithDelete(connection); - connection.execute("create index idx on person (id)"); - testMaxMinWithDelete(connection); - connection.executeUpdate("drop index * on person"); - testMaxMinWithDelete(connection); - - connection.executeUpdate("delete person where name > 'name0'"); - testEmptyTable(connection); - connection.purge("person"); - testEmptyTable(connection); - } -} - diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestMultipleConnection.java b/LitebaseSDK/src/java/samples/sys/testcases/TestMultipleConnection.java deleted file mode 100644 index 4414d7bae6..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestMultipleConnection.java +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; - -/** - * Tests Litebase with multiple connections, to access different databases at the same time. - */ -public class TestMultipleConnection extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - String tempPath = Convert.appendPath(Settings.appPath, "temp"); - - // Tests different ways of writing paths. - TestConnections(tempPath + "/a"); - TestConnections(Convert.appendPath(tempPath, "\\b")); - } - - /** - * Tests 2 different connections at the same time. - * - * @param tempPath The path of one of the connections used. - */ - private void TestConnections(String tempPath) - { - String dataPath = Settings.appPath; - - LitebaseConnection driver1 = AllTests.getInstance("Test", dataPath); // The first connection is on datapath. - if (driver1.exists("person")) // Drops the table of the first connection. - driver1.executeUpdate("drop table person"); - - if (Settings.nvfsVolume != -1) // Appends the volume number to the path. - tempPath = Settings.nvfsVolume + (':' + tempPath); - LitebaseConnection driver2 = AllTests.getInstance("Test", tempPath); // The second connection is on tempPath. - if (driver2.exists("person")) // Drops the table of the second connection. - driver2.executeUpdate("drop table person"); - - // Both tables can't exist yet. - assertFalse(driver1.exists("person")); - assertFalse(driver2.exists("person")); - - // After creating the table in the first connection, only this one can exists. - driver1.execute("create table person (id int, name char(10))"); - assertTrue(driver1.exists("person")); - assertFalse(driver2.exists("person")); - - // After creating the table in the second connection, both must exist. - driver2.execute("create table person (id int, name char(10), age int)"); - assertTrue(driver1.exists("person")); - assertTrue(driver2.exists("person")); - - // Both rows must be inserted successfully. - assertEquals(1, driver1.executeUpdate("insert into person values (0, '0')")); - assertEquals(1, driver2.executeUpdate("insert into person values (1, '1', 1)")); - - // Both rows must be found in the queries. - ResultSet rs1 = driver1.executeQuery("select * from person"); - ResultSet rs2 = driver2.executeQuery("select * from person"); - assertEquals(1, rs1.getRowCount()); - assertEquals(1, rs2.getRowCount()); - assertTrue(rs1.next()); - assertEquals("0", rs1.getString("name")); - assertTrue(rs2.next()); - assertEquals("1", rs2.getString("name")); - rs2.close(); - - // Inserts the row of the table of the first connection in the one of the second connection. - assertEquals(1, driver2.executeUpdate("insert into person values('" + rs1.getString("id") + "','" + rs1.getString("name") + "', 2)")); - - // A query on the table of the second connection must return all its rows. - assertTrue((rs2 = driver2.executeQuery("select * from person")).next()); - assertEquals(1, rs2.getInt("age")); - assertTrue(rs2.next()); - assertEquals(2, rs2.getInt("age")); - rs2.close(); - -// assertEquals(-1, driver1.getSlot()); -// assertEquals(-1, driver2.getSlot()); - - // Closes both connections. - driver1.closeAll(); - driver2.closeAll(); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestNullAndDefaultValues.java b/LitebaseSDK/src/java/samples/sys/testcases/TestNullAndDefaultValues.java deleted file mode 100644 index 74930ba5f8..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestNullAndDefaultValues.java +++ /dev/null @@ -1,1243 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; -import totalcross.util.Date; - -/** - * This test verifies default and null values for Litebase - */ -public class TestNullAndDefaultValues extends TestCase -{ - LitebaseConnection driver; - - /** - * The main method of the test. - */ - public void testRun() - { - driver = AllTests.getInstance("Test"); - testSimpleInserts(); // Tests simple inserts with null and defaults. - testGettingNulls(); // Tests getting null values. - testUpdatingNulls(); // Tests updating null values. - testUpdatingWithBool(); // Tests updating with AND and OR boolean connectors. - testSelects(); // Tests selects. - testDefaultNull(); // Tests default null. - testPrimaryKeyNull(); // Tests primary key with null. - testOrderBy(); // Tests order by ordering. - testAggregationFunctions(); // Tests aggregation functions. - testFunctionsAndNulls(); // Tests functions and null values. - testPreparedStatements(); // Tests prepared tatements. - testLikeWithNull(); // Tests like with null in the table. - driver.closeAll(); - } - - /** - * Tests simple inserts with null and defaults. - */ - private void testSimpleInserts() - { - if (driver.exists("person")) // Drops table if it exists. - driver.executeUpdate("drop table person"); - - // Invalid default empty date and empty datetime, and invalid date and datetime. - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, birth Date default '', " - + "years DateTime not null)"); - fail("1"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime default '')"); - fail("2"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, birth Date default ' ', " - + "years DateTime not null)"); - fail("3"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime default ' ')"); - fail("4"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, birth Date default '2010/02/29', " - + "years DateTime not null)"); - fail("5"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime default '1981/06/06 24:08:01.234')"); - fail("6"); - } - catch (SQLParseException exception) {} - - // Tests default string bigger than the definition. - try - { - driver.execute("create table person(name char(6) primary key default 'rnovais' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("7"); - } - catch (SQLParseException exception) {} - - // Tests short values out of range. - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age short default 32768, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("8"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age short default -32769, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("9"); - } - catch (SQLParseException exception) {} - - // Tests int values out of range. - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 2147483648, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("10"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default -2147483649, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("11"); - } - catch (SQLParseException exception) {} - - // Tests long values out of range. - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age long default 9223372036854775808, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("12"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age long default -9223372036854775809, " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("13"); - } - catch (SQLParseException exception) {} - - // Wrong default value types; - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 'rnovais', " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("14"); - } - catch (SQLParseException exception) {} - try - { - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age float default 'rnovais', " - + "birth Date default '1981/06/06', years DateTime not null)"); - fail("15"); - } - catch (SQLParseException exception) {} - - // Tests default string with its strict size. - driver.execute("create table person(name char(7) primary key default 'rnovais' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - - // Tests short values in the limit of the range. - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age short default +32767, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age short default -32768, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - - // Tests int values in the limit of the range. - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default +2147483647, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default -2147483648, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - - // Tests long values in the limit of the range. - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age long default +9223372036854775807, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age long default -9223372036854775808, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("drop table person"); - - // Creates and populates the table. - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int default 10, " - + "birth Date default '1981/06/06', years DateTime not null)"); - driver.executeUpdate("insert into person(name, birth, years) values ('renato novais', '1985/02/28',' 2006/08-21 12:08:01:234 ')"); - driver.executeUpdate("insert into person(birth, years) values ( '1985/02/28',' 2006/08-21 12:08:01:234 ')"); // Not null with default. - driver.executeUpdate("insert into person(name,age, years) values ('indira gomes', 14,' 2006/08-21 12:08:01:234 ')"); - driver.executeUpdate("insert into person(name, years) values ('caio novais', ' 2006/08-21 12:08:01:234 ')"); - - try // It is not possible not to insert a value in a column with a non-null restriction and no default value. - { - driver.executeUpdate("insert into person(name) values ('caio novais')"); - fail("16"); - } - catch (DriverException exception) {} - - // Queries the table. - ResultSet rs = driver.executeQuery("Select * from person"); - assertEquals(4, rs.getRowCount()); - String expectedTime = (new Time(2006, 8, 21, 12, 8, 1, 234)).toString(); - String expectedDate = Date.formatDate(28, 02, 1985, Settings.DATE_DMY); - - // Checks the results. - assertTrue(rs.next()); - assertEquals("renato novais", rs.getString("name")); - assertEquals(10, rs.getInt("age")); - assertEquals(expectedDate, rs.getDate(3).toString(Settings.DATE_DMY)); - assertEquals(expectedTime, rs.getDateTime(4).toString()); - - assertTrue(rs.next()); - assertEquals("rnovais", rs.getString("name")); - assertEquals(10, rs.getInt("age")); - assertEquals(expectedDate, rs.getDate(3).toString(Settings.DATE_DMY)); - assertEquals(expectedTime, rs.getDateTime(4).toString()); - - assertTrue(rs.next()); - assertEquals("indira gomes", rs.getString("name")); - assertEquals(14, rs.getInt("age")); - assertEquals(expectedDate = Date.formatDate(06, 06, 1981, Settings.DATE_DMY), rs.getDate(3).toString(Settings.DATE_DMY)); - assertEquals(expectedTime, rs.getDateTime(4).toString()); - - assertTrue(rs.next()); - assertEquals("caio novais", rs.getString("name")); - assertEquals(10, rs.getInt("age")); - assertEquals(expectedDate, rs.getDate(3).toString(Settings.DATE_DMY)); - assertEquals(expectedTime, rs.getDateTime(4).toString()); - rs.close(); - - // juliana@202_12: The default values must have been correctly saved on the table. - driver.closeAll(); - driver = AllTests.getInstance("Test"); - assertEquals(4, (rs = driver.executeQuery("Select * from person")).getRowCount()); - rs.close(); - } - - /** - * Tests getting null values. - */ - private void testGettingNulls() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates and populates the table. - driver.execute("create table person(field0 char(20),field1 short, field2 int, field3 long, field4 float, field5 double, field6 date, " - + "field7 DateTime)"); - driver.executeUpdate("insert into person values (null,null,null,null,null,null,null,null)"); - driver.executeUpdate("insert into person values (null,null,null,null,null,null,null,null)"); - driver.executeUpdate("delete person where rowid = 2"); - driver.purge("person"); - - - ResultSet rs = driver.executeQuery("Select * from person"); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertNull(rs.getString("field0")); - assertEquals(0, rs.getShort("field1")); - assertEquals(0, rs.getInt("field2")); - assertEquals(0, rs.getLong("field3")); - assertEquals(0, rs.getFloat("field4"), 1e-2); - assertEquals(0, rs.getDouble("field5"), 1e-2); - assertNull(rs.getDate("field6")); - assertNull(rs.getDateTime("field7")); - - // All the fields must be null. - assertTrue(rs.isNull("field0")); - assertTrue(rs.isNull("field1")); - assertTrue(rs.isNull("field2")); - assertTrue(rs.isNull("field3")); - assertTrue(rs.isNull("field4")); - assertTrue(rs.isNull("field5")); - assertTrue(rs.isNull("field6")); - assertTrue(rs.isNull("field7")); - assertTrue(rs.isNull(1)); - assertTrue(rs.isNull(2)); - assertTrue(rs.isNull(3)); - assertTrue(rs.isNull(4)); - assertTrue(rs.isNull(5)); - assertTrue(rs.isNull(6)); - assertTrue(rs.isNull(7)); - assertTrue(rs.isNull(8)); - - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("Select * from person where field0 is null and field1 is null and field2 is null and field3 is null " - + "and field4 is null and field5 is null and field6 is null")).getRowCount()); - assertTrue(rs.next()); - assertNull(rs.getString("field0")); - assertEquals(0, rs.getShort("field1")); - assertEquals(0, rs.getInt("field2")); - assertEquals(0, rs.getLong("field3")); - assertEquals(0, rs.getFloat("field4"), 1e-2); - assertEquals(0, rs.getDouble("field5"), 1e-2); - assertNull(rs.getDate("field6")); - assertNull(rs.getDateTime("field7")); - - // All the fields must be null. - assertTrue(rs.isNull("field0")); - assertTrue(rs.isNull("field1")); - assertTrue(rs.isNull("field2")); - assertTrue(rs.isNull("field3")); - assertTrue(rs.isNull("field4")); - assertTrue(rs.isNull("field5")); - assertTrue(rs.isNull("field6")); - assertTrue(rs.isNull("field7")); - assertTrue(rs.isNull(1)); - assertTrue(rs.isNull(2)); - assertTrue(rs.isNull(3)); - assertTrue(rs.isNull(4)); - assertTrue(rs.isNull(5)); - assertTrue(rs.isNull(6)); - assertTrue(rs.isNull(7)); - assertTrue(rs.isNull(8)); - - rs.close(); - } - - /** - * Tests updating null values. - */ - private void testUpdatingNulls() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates and populates the table. - driver.execute("create table person(name char(20) default '', age int default 10)"); - driver.execute("create index idx on person(name)"); - driver.executeUpdate("insert into person( name, age) values ('InDiRa GoMeS', null)"); - - // Closes and reopens Litebase to test that the default values data will be saved correctly. - driver.closeAll(); - driver = AllTests.getInstance("Test"); - - ResultSet rs = driver.executeQuery("Select age, name from person where name = 'InDiRa GoMeS'"); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("InDiRa GoMeS", rs.getString("name")); - assertEquals(0, rs.getInt("age")); - rs.close(); - - // Tests an update. - driver.executeUpdate("update person set age = 21, name = null"); - rs = driver.executeQuery("Select age, name from person"); - assertEquals(1, (rs = driver.executeQuery("Select age, name from person")).getRowCount()); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals(21, rs.getInt("age")); - rs.close(); - - // Updates the table with a not null value. - driver.executeUpdate("update person set name = 'juliana', age = 32 where name is null"); - rs = driver.executeQuery("Select age, name from person"); - assertEquals(1, (rs = driver.executeQuery("Select age, name from person")).getRowCount()); - assertTrue(rs.next()); - assertNotNull(rs.getString("name")); - assertEquals(32, rs.getInt("age")); - rs.close(); - - // Tests empty default value. - driver.executeUpdate("insert into person(age) values (null)"); - assertEquals(2, (rs = driver.executeQuery("Select age, name from person where name = 'juliana' or name = ''")).getRowCount()); - assertTrue(rs.next()); - assertEquals("juliana", rs.getString("name")); - assertEquals(32, rs.getInt("age")); - assertTrue(rs.next()); - assertEquals("", rs.getString("name")); - assertEquals(0, rs.getInt("age")); - rs.close(); - } - - /** - * Tests updating with AND and OR boolean connectors. - */ - private void testUpdatingWithBool() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates, populates the table. - driver.execute("create table person(name char(20) default 'ZeneS', age int)"); - driver.execute("create index idx on person (name, age)"); - driver.executeUpdate("insert into person( age, name) values (21, 'renato')"); - driver.executeUpdate("insert into person( age, name) values (null, 'maria')"); - - // Updates the table. - driver.executeUpdate("update person set age = 12, name = null where age is not null and name is null"); - driver.executeUpdate("update person set age = 12, name = null where age is not null and name is not null"); - driver.executeUpdate("update person set age = 12, name = null where name = 'joao' or age is not null"); - driver.executeUpdate("update person set age = null, name = 'zENEs' where name is null and age = 12"); - - ResultSet rs = driver.executeQuery("Select age, name from person"); - assertEquals(2, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("zENEs", rs.getString("name")); - assertEquals(0, rs.getInt("age")); - assertTrue(rs.next()); - assertEquals("maria", rs.getString("name")); - assertEquals(0, rs.getInt("age")); - rs.close(); - - assertEquals(0, (rs = driver.executeQuery("Select name, age from person where name = 'zENEs' and age = 0")).getRowCount()); - rs.close(); - assertEquals(0, (rs = driver.executeQuery("Select name, age from person where name = 'maria' and age = 0")).getRowCount()); - rs.close(); - - driver.executeUpdate("delete from person"); // Empties the table. - - // Re-populates the table. - driver.executeUpdate("insert into person( age, name) values (21, 'renato')"); - driver.executeUpdate("insert into person( age, name) values (null, 'jener')"); - - // Updates the table. - driver.executeUpdate("update person set age = null, name = 'indira' where age is null"); - driver.executeUpdate("update person set name = 'TI�oZ�o' where age is not null"); - - assertEquals(2, (rs = driver.executeQuery("Select name, age from person")).getRowCount()); - assertTrue(rs.next()); - assertEquals("TI�oZ�o", rs.getString("name")); - assertEquals(21, rs.getInt("age")); - assertTrue(rs.next()); - assertEquals("indira", rs.getString("name")); - assertEquals(0, rs.getInt("age")); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("Select name, age from person where name = 'TI�oZ�o' and age = 21")).getRowCount()); - assertTrue(rs.next()); - assertEquals("TI�oZ�o", rs.getString("name")); - assertEquals(21, rs.getInt("age")); - rs.close(); - assertEquals(0, (rs = driver.executeQuery("Select name, age from person where name = 'indira' and age = 0")).getRowCount()); - rs.close(); - } - - /** - * Tests selects. - */ - private void testSelects() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates and populates the table. - driver.execute("create table person(name char(20) primary key default 'rnovais' not null, age int, birth DATE default '1994/05/06')"); - driver.executeUpdate("insert into person( age, name) values (21, 'maria')"); - driver.executeUpdate("insert into person( name) values ('parazita')"); - driver.executeUpdate("insert into person( age, birth) values (30,null)"); - driver.executeUpdate("insert into person( name, age) values ('VUlcao',5000)"); - driver.executeUpdate("insert into person( name) values ('Plinio')"); - - try // Primary key violation. - { - - driver.executeUpdate("insert into person( age, birth) values (17,'2005/03/12')"); - fail("17"); - } - catch(PrimaryKeyViolationException exception) {} - - // More inserts. - driver.executeUpdate("insert into person( name, age, birth) values ('Maria Beatriz',1,null)"); - driver.executeUpdate("insert into person( name, age, birth) values ('Caio', null, null)"); - driver.executeUpdate("insert into person( name, age, birth) values ('Lucas', null, '2020/11/30')"); - driver.executeUpdate("insert into person( birth, name) values ('1998/12/23', 'celia')"); - driver.executeUpdate("insert into person values ('Marlene',10,'2000/02/04')"); - - ResultSet rs = driver.executeQuery("Select * from person where name is null"); - assertEquals(0, rs.getRowCount()); - rs.close(); - - assertEquals(10, (rs = driver.executeQuery("Select * from person where name is not null")).getRowCount()); - rs.close(); - - assertEquals(5, (rs = driver.executeQuery("Select * from person where age is not null")).getRowCount()); - assertTrue(rs.next()); - - assertEquals(6, rs.getDate("birth").getDay()); - assertEquals(5, rs.getDate("birth").getMonth()); - assertEquals(1994, rs.getDate("birth").getYear()); - - assertEquals(3, (rs = driver.executeQuery("Select * from person where birth is null")).getRowCount()); - assertTrue(rs.next()); - assertEquals("rnovais", rs.getString("name")); - assertTrue(rs.next()); - assertEquals("Maria Beatriz", rs.getString("name")); - assertEquals(1, rs.getInt("age")); - assertNull(rs.getString("birth")); - assertTrue(rs.next()); - assertEquals("Caio", rs.getString("name")); - rs.close(); - - assertEquals(3, (rs = driver.executeQuery("Select * from person where name is not null and age is not null and birth is not null")).getRowCount()); - assertTrue(rs.next()); - assertEquals("maria", rs.getString("name")); - assertTrue(rs.next()); - assertEquals("VUlcao", rs.getString("name")); - assertEquals(5000, rs.getInt("age")); - assertEquals(6, rs.getDate("birth").getDay()); - assertEquals(5, rs.getDate("birth").getMonth()); - assertEquals(1994, rs.getDate("birth").getYear()); - assertTrue(rs.next()); - assertEquals("Marlene", rs.getString("name")); - rs.close(); - } - - /** - * Tests default null. - */ - private void testDefaultNull() - { - // Drops and re-creates the table. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (name char(20) default null not null, age int default 21)"); - - try // Can't insert a null when the column can't have a null. - { - driver.executeUpdate("insert into person values (null,null)"); - fail("18"); - } - catch (DriverException exception) {} - - driver.executeUpdate("insert into person values ('maria',22)"); - - try // Can't update to a null when the column can't have a null. - { - driver.executeUpdate("update person set name = null, age = 21"); - fail("19"); - } - catch (DriverException e) {} - } - - /** - * Tests primary key with null. - */ - private void testPrimaryKeyNull() - { - // Drops and re-creates the table. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (name char(20) primary key , age int default 21)"); - - try // A primary key can't be null. - { - driver.executeUpdate("insert into person values (null,null)"); - fail("20"); - } - catch (DriverException exception) {} - try // A primary key can't be null. - { - driver.executeUpdate("insert into person(age) values (null)"); - fail("21"); - } - catch (DriverException exception) {} - try // A primary key can't be update to null. - { - driver.executeUpdate("update person set name = null, age = 21"); - fail("22"); - } - catch (DriverException exception) {} - - // Drops the primary key and inserts a null. - driver.executeUpdate("alter table person drop primary key"); - driver.executeUpdate("insert into person values (null, null)"); - - try // Now, the primary key can't be created because of a null in it. - { - driver.executeUpdate("alter table person add primary key(name)"); - fail("23"); - } - catch (DriverException exception) {} - } - - /** - * Tests order by ordering. - */ - private void testOrderBy() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates and populates the table. - driver.execute("create table PERSON (name char(20) , age1 int, age2 int)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Joao Pedro',10, 20)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Caio',null, 30)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Lucas',8, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Maria',null, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('carol',23, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('null',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('danilo',null, 27)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('felipe',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,23, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('indira',null, 29)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('felipe',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,23, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('caio',null, 25)"); - - ResultSet rs = driver.executeQuery("Select name, age1, age2 from person order by name"); - assertTrue(rs.next()); - assertEquals("Caio", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("30", rs.getString("age2")); - assertFalse(rs.isNull("name")); - assertTrue(rs.isNull("age1")); - assertFalse(rs.isNull("age2")); - assertTrue(rs.next()); - assertEquals("Joao Pedro", rs.getString("name")); - assertEquals("10", rs.getString("age1")); - assertEquals("20", rs.getString("age2")); - assertFalse(rs.isNull("name")); - assertFalse(rs.isNull("age1")); - assertFalse(rs.isNull("age2")); - assertTrue(rs.next()); - assertEquals("Lucas", rs.getString("name")); - assertEquals("8", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertFalse(rs.isNull("name")); - assertFalse(rs.isNull("age1")); - assertTrue(rs.isNull("age2")); - assertTrue(rs.next()); - assertEquals("Maria", rs.getString("name")); - assertNull(rs.getString("age1")); - assertNull(rs.getString("age2")); - assertFalse(rs.isNull("name")); - assertTrue(rs.isNull("age1")); - assertTrue(rs.isNull("age2")); - assertTrue(rs.next()); - assertEquals("caio", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("25", rs.getString("age2")); - assertFalse(rs.isNull("name")); - assertTrue(rs.isNull("age1")); - assertFalse(rs.isNull("age2")); - assertTrue(rs.next()); - assertEquals("carol", rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertFalse(rs.isNull("name")); - assertFalse(rs.isNull("age1")); - assertTrue(rs.isNull("age2")); - rs.close(); - - assertTrue((rs = driver.executeQuery("Select name, age1, age2 from person order by name desc, age1, age2")).next()); - assertNull(rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertTrue(rs.isNull(1)); - assertFalse(rs.isNull(2)); - assertTrue(rs.isNull(3)); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertTrue(rs.isNull(1)); - assertFalse(rs.isNull(2)); - assertTrue(rs.isNull(3)); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("12", rs.getString("age2")); - assertTrue(rs.isNull(1)); - assertTrue(rs.isNull(2)); - assertFalse(rs.isNull(3)); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("12", rs.getString("age2")); - assertFalse(rs.isNull(1)); - assertTrue(rs.isNull(2)); - assertFalse(rs.isNull(3)); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("12", rs.getString("age2")); - assertFalse(rs.isNull(1)); - assertTrue(rs.isNull(2)); - assertFalse(rs.isNull(3)); - assertTrue(rs.next()); - assertEquals("null", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("12", rs.getString("age2")); - assertFalse(rs.isNull(1)); - assertTrue(rs.isNull(2)); - assertFalse(rs.isNull(3)); - rs.close(); - } - - /** - * Tests aggregation functions. - */ - private void testAggregationFunctions() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates and populates the table. - driver.execute("create table PERSON (name char(20) , age1 int, age2 int)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('maria',10, 20)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('maria',null, 30)"); - - ResultSet rs = driver.executeQuery("Select name, avg(age1) as a1, avg(age2) as a2 from person group by name"); - assertTrue(rs.next()); - assertEquals("10.0", rs.getString("a1")); - assertEquals("25.0", rs.getString("a2")); - rs.close(); - - driver.executeUpdate("delete from person where age1 is null"); - driver.executeUpdate("insert into person(name, age1, age2) values ('maria',8, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('maria',null, null)"); - - assertTrue((rs = driver.executeQuery("Select name, avg(age1) as a1, avg(age2) as a2 from person group by name")).next()); - assertEquals("9.0", rs.getString("a1")); - assertEquals("20.0", rs.getString("a2")); - rs.close(); - - // Empties and re-populates the table again. - driver.executeUpdate("delete from person"); - driver.executeUpdate("insert into person(name, age1, age2) values ('maria',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('maria',23, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato',null, 24)"); - - assertTrue((rs = driver.executeQuery("Select name, avg(age1) as a1, avg(age2) as a2 from person group by name order by name")).next()); - assertEquals("23.0", rs.getString("a1")); - assertEquals("12.0", rs.getString("a2")); - assertTrue(rs.next()); - assertNull(rs.getString("a1")); - assertEquals("18.0", rs.getString("a2")); - rs.close(); - - assertTrue((rs = driver.executeQuery("Select count(*) as av from person")).next()); - assertEquals("4", rs.getString("av")); - rs.close(); - - driver.executeUpdate("update person set age1 = -23 where age2 is null"); - assertTrue((rs = driver.executeQuery(("Select name, count(*) as a, max(age1) as a1, min(age2) as a2 from person group by name"))).next()); - assertEquals("maria", rs.getString("name")); - assertEquals("2", rs.getString("a")); - assertEquals("-23", rs.getString("a1")); - assertEquals("12", rs.getString("a2")); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertEquals("2", rs.getString("a")); - assertNull(rs.getString("a1")); - assertEquals("12", rs.getString("a2")); - rs.close(); - - // Re-creates the table and populates it. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (name char(20) , age1 char(20), age2 int)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Joao Pedro',null, 20)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,'23', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Joao Pedro',null, 30)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,'23', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Joao Pedro',null, 50)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,'23', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,'23', null)"); - - assertTrue((rs = driver.executeQuery("Select name, age1, count(*) as cnt from person group by name, age1")).next()); - assertEquals("Joao Pedro", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals(3, rs.getInt("cnt")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertEquals(4, rs.getInt("cnt")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - rs.close(); - - driver.executeUpdate("delete from person where name is null or name is not null"); // Deletes all - - // Re-populates the table. - driver.executeUpdate("insert into person(name, age1, age2) values ('Joao Pedro',null, 20)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('caio',null, 30)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Lucas','8', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('Maria',null, null)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('carol','23', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('null',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('danilo',null, 28)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('felipe',null, 16)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,'23', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato','85', 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato',null, 84)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('indira',null, 05)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('felipe',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('null',null, 10)"); - driver.executeUpdate("insert into person(name, age1, age2) values (null,'23', null)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('renato',null, 12)"); - driver.executeUpdate("insert into person(name, age1, age2) values ('caio',null, 24)"); - - assertTrue((rs = driver.executeQuery("Select name, age1, count(*) as cnt from person group by name, age1")).next()); - assertEquals("Joao Pedro", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals(1, rs.getInt("cnt")); - assertTrue(rs.next()); - assertEquals("Lucas", rs.getString("name")); - assertEquals("8", rs.getString("age1")); - assertEquals(1, rs.getInt("cnt")); - assertTrue(rs.next()); - assertEquals("Maria", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("caio", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("carol", rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("danilo", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("felipe", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("indira", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("null", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertEquals("85", rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - rs.close(); - - // having - assertTrue((rs = driver.executeQuery("Select name, age1, avg(age2) as cnt from person group by name, age1 having cnt is not null")).next()); - assertEquals("Joao Pedro", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("20.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("caio", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("27.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("danilo", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("28.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("felipe", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("14.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("indira", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("5.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("null", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("11.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertEquals("85", rs.getString("age1")); - assertEquals("12.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("48.0", rs.getString("cnt")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("12.0", rs.getString("cnt")); - rs.close(); - - assertTrue((rs = driver.executeQuery("Select name, age1, avg(age2) as cnt from person group by name, age1 having cnt is null")).next()); - assertEquals("Lucas", rs.getString("name")); - assertEquals("8", rs.getString("age1")); - assertNull(rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("Maria", rs.getString("name")); - assertNull(rs.getString("age1")); - assertNull(rs.getString("cnt")); - assertTrue(rs.next()); - assertEquals("carol", rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertNull(rs.getString("cnt")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertNull(rs.getString("cnt")); - rs.close(); - } - - /** - * Tests functions and null values. - */ - private void testFunctionsAndNulls() - { - driver.executeUpdate("drop table person"); // Drops the table. - - // Creates and populates the table. - driver.execute("create table PERSON (name char(20) , age1 int, age2 int)"); - driver.executeUpdate("insert into person values ('Joao Pedro',null, 20)"); - driver.executeUpdate("insert into person values ('caio',null, 30)"); - driver.executeUpdate("insert into person values ('Lucas',8, null)"); - driver.executeUpdate("insert into person values ('Maria',null, null)"); - driver.executeUpdate("insert into person values (null,null, 12)"); - driver.executeUpdate("insert into person values ('carol','23', null)"); - driver.executeUpdate("insert into person values ('null',null, 12)"); - driver.executeUpdate("insert into person values ('danilo',null, 28)"); - driver.executeUpdate("insert into person values ('felipe',null, 16)"); - driver.executeUpdate("insert into person values (null,23, null)"); - driver.executeUpdate("insert into person values ('renato',85, 12)"); - driver.executeUpdate("insert into person values ('renato',null, 84)"); - driver.executeUpdate("insert into person values ('indira',null, 05)"); - driver.executeUpdate("insert into person values ('felipe',null, 12)"); - driver.executeUpdate("insert into person values ('null',null, 10)"); - driver.executeUpdate("insert into person values (null,23, null)"); - driver.executeUpdate("insert into person values ('renato',null, 12)"); - driver.executeUpdate("insert into person values ('caio',null, 24)"); - - ResultSet rs = driver.executeQuery("Select name, age1, count(*) as cnt from person group by name, age1"); - assertTrue(rs.next()); - assertEquals("Joao Pedro", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals(1, rs.getInt("cnt")); - assertEquals("Joao Pedro\t\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("Lucas", rs.getString("name")); - assertEquals("8", rs.getString("age1")); - assertEquals(1, rs.getInt("cnt")); - assertEquals("Lucas\t8\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("Maria", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertEquals("Maria\t\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("caio", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertEquals("caio\t\t2", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("carol", rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertEquals("carol\t23\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("danilo", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertEquals("danilo\t\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("felipe", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertEquals("felipe\t\t2", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("indira", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertEquals("indira\t\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("null", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertEquals("null\t\t2", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertEquals("85", rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertEquals("renato\t85\t1", rs.rowToString()); - assertTrue(rs.next()); - assertEquals("renato", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertEquals("renato\t\t2", rs.rowToString()); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("23", rs.getString("age1")); - assertEquals("2", rs.getString("cnt")); - assertEquals("\t23\t2", rs.rowToString()); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("1", rs.getString("cnt")); - assertEquals("\t\t1", rs.rowToString()); - rs.close(); - } - - /** - * Tests prepared statements. - */ - private void testPreparedStatements() - { - LitebaseConnection driver = AllTests.getInstance("Test"); // Gets another connection. - - // Re-creates the table. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (name char(20) , age1 int, age2 int)"); - - // Populates the table. - PreparedStatement ps = driver.prepareStatement("insert into person(age1, age2, name) values (?,?,?)"); - ps.setNull(0); - ps.setString(1, null); - ps.setString(2, "Renato"); - ps.executeUpdate(); - ps.setInt(0, 12); - ps.setString(1, null); - ps.setNull(2); - ps.executeUpdate(); - ps.setInt(0, 15); - ps.setString(1, null); - ps.setString(2, "Null"); - ps.executeUpdate(); - - ResultSet rs = driver.executeQuery("Select name, age1, age2 from person order by name"); - assertTrue(rs.next()); - assertEquals("Null", rs.getString("name")); - assertEquals("15", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString("name")); - assertNull(rs.getString("age1")); - assertNull(rs.getString("age2")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("12", rs.getString("age1")); - assertNull(rs.getString("age2")); - rs.close(); - - // Re-creates the table. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (name char(20) , age1 int , age2 int default 50, age3 int not null)"); - - // Populates the table. - (ps = driver.prepareStatement("insert into person( age1, age3, age2, name) values (?,?,?,?)")).setString(3,"Jo�o Pedro"); - ps.setString(0, "21"); - ps.setString(1, "156"); - ps.executeUpdate(); - ps.clearParameters(); - ps.setString(3, "Renato Novais"); - ps.setString(0, "25"); - ps.setString(2, null); - ps.setString(1, "11"); - ps.executeUpdate(); - ps.clearParameters(); - ps.setString(3, "Indira"); - ps.setString(2, "12"); - ps.setString(1, "10"); - ps.executeUpdate(); - - try // age3 can't be null. - { - ps.clearParameters(); - ps.setString(3, "caio"); - ps.setString(2, "100"); - ps.setString(1, null); - ps.executeUpdate(); - fail("24"); - } - catch (DriverException exception) {} - - try // age3 can't be null. - { - ps.clearParameters(); - ps.setString(3, "caio"); - ps.setString(2, "100"); - ps.executeUpdate(); - fail("25"); - } - catch (DriverException exception) {} - - assertTrue((rs = driver.executeQuery("Select name, age1, age2, age3 from person")).next()); - assertEquals("Jo�o Pedro", rs.getString("name")); - assertEquals("21", rs.getString("age1")); - assertEquals("50", rs.getString("age2")); - assertEquals("156", rs.getString("age3")); - assertTrue(rs.next()); - assertEquals("Renato Novais", rs.getString("name")); - assertEquals("25", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertEquals("11", rs.getString("age3")); - assertTrue(rs.next()); - assertEquals("Indira", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("12", rs.getString("age2")); - assertEquals("10", rs.getString("age3")); - rs.close(); - - (ps = driver.prepareStatement("update person set age1 = ?, name = ?, age2=? where name = ? ")).clearParameters(); - ps.setString(1, "Caio"); - ps.setString(0, "26"); - ps.setString(2, null); - ps.setString(3, "Renato Novais"); - ps.executeUpdate(); - - (ps = driver.prepareStatement("update person set name = ?, age1 = ? where name = ? ")).clearParameters(); - ps.setString(0, "Carolina"); - ps.setString(1, null); - ps.setString(2, "Jo�o Pedro"); - ps.executeUpdate(); - - (ps = driver.prepareStatement("update person set age1 = ?, age3 = ?, name = ?, age2 = ? where name = ? ")).clearParameters(); - ps.setString(0, "21"); - ps.setString(1, "23"); - ps.setNull(2); - ps.setString(3, "22"); - ps.setString(4, "Indira"); - ps.executeUpdate(); - - try // age3 can't be null. - { - ps.clearParameters(); - ps.setString(0, "21"); - ps.setString(1, null); - ps.setString(2, null); - ps.setString(3, "22"); - ps.setString(4, "Indira"); - ps.executeUpdate(); - fail("26"); - } - catch (DriverException exception) {} - - assertTrue((rs = driver.executeQuery("Select name, age1, age2, age3 from person")).next()); - assertEquals("Carolina", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("50", rs.getString("age2")); - assertEquals("156", rs.getString("age3")); - assertTrue(rs.next()); - assertEquals("Caio", rs.getString("name")); - assertEquals("26", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertEquals("11", rs.getString("age3")); - assertTrue(rs.next()); - assertNull(rs.getString("name")); - assertEquals("21", rs.getString("age1")); - assertEquals("22", rs.getString("age2")); - assertEquals("23", rs.getString("age3")); - rs.close(); - - (ps = driver.prepareStatement("update person set name = ? where name is null")).setString(0,"Danilo"); - ps.executeUpdate(); - ps.setString(0, "Felipe"); // No action, since name is not null. - ps.executeUpdate(); - - assertTrue((rs = driver.executeQuery("Select name, age1, age2, age3 from person")).next()); - assertEquals("Carolina", rs.getString("name")); - assertNull(rs.getString("age1")); - assertEquals("50", rs.getString("age2")); - assertEquals("156", rs.getString("age3")); - assertTrue(rs.next()); - assertEquals("Caio", rs.getString("name")); - assertEquals("26", rs.getString("age1")); - assertNull(rs.getString("age2")); - assertEquals("11", rs.getString("age3")); - assertTrue(rs.next()); - assertEquals("Danilo", rs.getString("name")); - assertEquals("21", rs.getString("age1")); - assertEquals("22", rs.getString("age2")); - assertEquals("23", rs.getString("age3")); - rs.close(); - } - - /** - * Tests like with null in the table. - */ - private void testLikeWithNull() - { - // Recreates the table. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (birth datetime default '1981/06/06')"); - - // Populates the table with a null. - PreparedStatement ps = driver.prepareStatement("INSERT INTO PERSON (birth) VALUES (?)"); - assertEquals(1, ps.executeUpdate()); - ps.setString(0, "1980/06/06"); - assertEquals(1, ps.executeUpdate()); - ps.setNull(0); - assertEquals(1, ps.executeUpdate()); - - // There can't be any rows in the result set. - ResultSet rs = driver.executeQuery("select * from person where birth like '1981/06/06 %'"); - assertEquals(1, rs.getRowCount()); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where birth like '1980/06/06 %'")).getRowCount()); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where birth is null")).getRowCount()); - rs.close(); - - assertEquals(2, (rs = driver.executeQuery("select * from person where birth is not null")).getRowCount()); - rs.close(); - - // Repeats the test using an index. - driver.execute("create index idx on person(birth)"); - assertEquals(1, (rs = driver.executeQuery("select * from person where birth like '1980/06/06 %'")).getRowCount()); - rs.close(); - - assertEquals(1, (rs = driver.executeQuery("select * from person where birth is null")).getRowCount()); - rs.close(); - - assertEquals(2, (rs = driver.executeQuery("select * from person where birth is not null")).getRowCount()); - rs.close(); - - // Can't accept = null or != null. - try - { - driver.executeQuery("select * from person where birth != null"); - fail("27"); - } - catch (SQLParseException exception) {} - try - { - driver.executeQuery("select * from person where birth = null"); - fail("28"); - } - catch (SQLParseException exception) {} - } - -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestOrderBy.java b/LitebaseSDK/src/java/samples/sys/testcases/TestOrderBy.java deleted file mode 100644 index 71034cec54..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestOrderBy.java +++ /dev/null @@ -1,641 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests that order by always order the table in the correct order. - */ -public class TestOrderBy extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - // Recreates the table. - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - driver.execute("create table person (name char(10), age int, district char(10))"); - - // Populates the table. - driver.executeUpdate("insert into person values ('Juliana', 28, 'Maracan�')"); - driver.executeUpdate("insert into person values ('Roberta', 26, 'Maracan�')"); - driver.executeUpdate("insert into person values ('Guilherme', 37, 'Copacabana')"); - driver.executeUpdate("insert into person values ('Bruno', 23, 'Barra')"); - driver.executeUpdate("insert into person values ('C�lia', 57, 'Maracan�')"); - driver.executeUpdate("insert into person values ('Renato', 35, 'Flamengo')"); - driver.executeUpdate("insert into person values ('Ronaldo', 61, 'Maracan�')"); - driver.executeUpdate("insert into person values ('F�bio', 23, 'Jacar�')"); - driver.executeUpdate("insert into person values ('Vin�cius', 28, 'Ipanema')"); - driver.executeUpdate("insert into person values ('Renato', 23, 'Cacul�')"); - - driver.executeUpdate("insert into person values ('Renato', 23, 'Cacul�')"); - driver.executeUpdate("insert into person values ('Vin�cius', 28, 'Ipanema')"); - driver.executeUpdate("insert into person values ('F�bio', 23, 'Jacar�')"); - driver.executeUpdate("insert into person values ('Ronaldo', 61, 'Maracan�')"); - driver.executeUpdate("insert into person values ('Renato', 35, 'Flamengo')"); - driver.executeUpdate("insert into person values ('C�lia', 57, 'Maracan�')"); - driver.executeUpdate("insert into person values ('Bruno', 23, 'Barra')"); - driver.executeUpdate("insert into person values ('Guilherme', 37, 'Copacabana')"); - driver.executeUpdate("insert into person values ('Roberta', 26, 'Maracan�')"); - driver.executeUpdate("insert into person values ('Juliana', 28, 'Maracan�')"); - - // The selects. - ResultSet rs = driver.executeQuery("select * from person order by name desc, age asc, district"); - assertEquals(20, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - rs.close(); - - assertEquals((rs = driver.executeQuery("select * from person order by name asc, district desc, age")).getRowCount(), 20); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - rs.close(); - - assertEquals((rs = driver.executeQuery("select * from person order by age, name asc, district desc")).getRowCount(), 20); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - rs.close(); - - assertEquals((rs = driver.executeQuery("select * from person order by age asc, district, name desc")).getRowCount(), 20); - - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - rs.close(); - - assertEquals((rs = driver.executeQuery("select * from person order by district desc, name asc, age")).getRowCount(), 20); - - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - rs.close(); - - assertEquals((rs = driver.executeQuery("select * from person order by district asc, age, name desc")).getRowCount(), 20); - - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Bruno", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Barra", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Cacul�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Guilherme", rs.getString(1)); - assertEquals("37", rs.getString(2)); - assertEquals("Copacabana", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Renato", rs.getString(1)); - assertEquals("35", rs.getString(2)); - assertEquals("Flamengo", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Vin�cius", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Ipanema", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("F�bio", rs.getString(1)); - assertEquals("23", rs.getString(2)); - assertEquals("Jacar�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Roberta", rs.getString(1)); - assertEquals("26", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Juliana", rs.getString(1)); - assertEquals("28", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("C�lia", rs.getString(1)); - assertEquals("57", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - assertTrue(rs.next()); - assertEquals("Ronaldo", rs.getString(1)); - assertEquals("61", rs.getString(2)); - assertEquals("Maracan�", rs.getString(3)); - rs.close(); - - // Wrong queries. - try // Order by and group by must match. - { - driver.executeQuery("select * from person group by name order by district"); - fail("1"); - } - catch (SQLParseException exception) {} - try // Order by and group by must match. - { - driver.executeQuery("select * from person group by name order by name, district"); - fail("2"); - } - catch (SQLParseException exception) {} - try // All non-agreggate columns must be in the group by clause. - { - driver.executeQuery("select * from person group by name"); - fail("3"); - } - catch (SQLParseException exception) {} - try // All non-agreggate columns must be in the group by clause. - { - driver.executeQuery("select rowid, name, age, district from person group by name, age, district"); - fail("4"); - } - catch (SQLParseException exception) {} - - // These ones must work. - driver.executeQuery("select * from person group by name, age, district").close(); - driver.executeQuery("select rowid, name, age, district from person group by rowid, name, age, district").close(); - driver.executeQuery("select rowid, name, age, district from person group by rowid, name, age, district order by rowid, name, age, district").close(); - driver.executeQuery("select * from person order by rowid").close(); - driver.closeAll(); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestOrderByIndices.java b/LitebaseSDK/src/java/samples/sys/testcases/TestOrderByIndices.java deleted file mode 100644 index d0c49238ec..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestOrderByIndices.java +++ /dev/null @@ -1,485 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.Time; -import totalcross.unit.TestCase; - -/** - * Does tests with order by and group by queries using indices or not. - */ -public class TestOrderByIndices extends TestCase -{ - /** - * Main test method. - */ - public void testRun() - { - LitebaseConnection connection = LitebaseConnection.getInstance("Test"); - - if (connection.exists("person")) - connection.executeUpdate("drop table person"); - connection.execute("create table person (name char(10), cpf char(10), birth datetime)"); - PreparedStatement ps = connection.prepareStatement("insert into person values (?, ?, ?)"); - - testEmptyTable(connection); - - // Table without repetitions. - int i = 2000; - Time time = new Time(2011, 7, 19, 0, 0, 0, 0); - - connection.setRowInc("person", 2000); - while (--i >= 0) - { - ps.setString(0, "name" + i); - ps.setString(1, "cpf" + (1999 - i)); - ps.setDateTime(2, time); - ps.executeUpdate(); - time.inc(0, 0, 1); - } - connection.setRowInc("person", -1); - executeAllTests(connection); - - // Table with repetitions. - connection.executeUpdate("drop table person"); - connection.execute("create table person (name char(10) not null, cpf char(10) not null, birth datetime not null)"); - ps = connection.prepareStatement("insert into person values (?, ?, ?)"); - i = 2000; - time.inc(0, 0, -2000); - connection.setRowInc("person", 2000); - while (--i >= 0) - { - ps.setString(0, "name" + i); - ps.setString(1, "cpf" + (1999 - i)); - ps.setDateTime(2, time); - ps.executeUpdate(); - ps.executeUpdate(); - time.inc(0, 0, 1); - } - connection.setRowInc("person", -1); - executeAllTestsWithRep(connection); - connection.closeAll(); - } - - /** - * Test with empty or all rows deleted. - * - * @param connection The connection with Litebase. - */ - private void testEmptyOrderBy(LitebaseConnection connection) - { - ResultSet resultSet = connection.executeQuery("select name from person order by name"); - assertEquals(0, resultSet.getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select cpf from person order by cpf")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select birth from person order by birth")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select name, cpf from person order by name, cpf")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select cpf, name from person order by cpf, name")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select birth, rowid from person order by birth, rowid")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select rowid, birth from person order by rowid, birth")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select name from person where name = 'name0' group by name")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select cpf from person where cpf > 'cpf0' group by cpf")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select birth from person where birth != '2011/07/19' group by birth")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery( - "select name, cpf from person where name = 'name0' and cpf = 'cpf1999' group by name, cpf")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery( - "select name, cpf from person where name = 'name0' or cpf = 'cpf0' group by cpf, name")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery( - "select birth, rowid from person where birth like '2011/0%' group by birth, rowid")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals(0, (resultSet = connection.executeQuery("select birth, rowid from person where rowid > 0 group by rowid, birth")).getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Tests order by and group by with a populated table. - * - * @param connection The connection with Litebase. - */ - private void testOrderBy(LitebaseConnection connection) - { - ResultSet resultSet = connection.executeQuery("select name from person order by name"); - int count = resultSet.getRowCount(); - assertTrue(count == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select cpf from person order by cpf")).getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth from person order by birth")).getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select name, cpf from person order by name, cpf")) - .getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select cpf, name from person order by cpf, name")) - .getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth, rowid from person order by birth, rowid")) - .getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select rowid, birth from person order by rowid, birth")) - .getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select name from person where name = 'name0' group by name")).getRowCount(), 1); - testSortAsc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select cpf from person where cpf > 'cpf0' group by cpf")).getRowCount(), 1999); - testSortAsc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select birth from person where birth != '2011/07/19' group by birth")) - .getRowCount(), 1999); - testSortAsc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select name, cpf from person where name = 'name0' and cpf = 'cpf1999' group by name, cpf")) - .getRowCount(), 1); - testSortAsc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select cpf, name from person where name = 'name0' or cpf = 'cpf0' group by cpf, name")) - .getRowCount(), 2); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth, rowid from person where birth like '2011/0%' group by birth, rowid")) - .getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth, rowid from person where rowid > 0 group by rowid, birth")) - .getRowCount()) == 2000 || count == 4000); - testSortAsc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select rowid from person where rowid = 0 group by rowid")) - .getRowCount(), 0); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Tests order by and group by with some deletes. - * - * @param connection The connection with Litebase. - */ - private void testOrderByWithDelete(LitebaseConnection connection) - { - ResultSet resultSet = connection.executeQuery("select name from person order by name desc"); - int count = resultSet.getRowCount(); - assertTrue(count == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select cpf from person order by cpf desc")) - .getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth from person order by birth desc")) - .getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select name, cpf from person order by name desc, cpf desc")) - .getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select cpf, name from person order by cpf desc, name desc")) - .getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth, rowid from person order by birth desc, rowid desc")) - .getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select rowid, birth from person order by rowid desc, birth desc")) - .getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select name from person where name = 'name1' group by name order by name desc")) - .getRowCount(), 1); - testSortDesc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select cpf from person where cpf > 'cpf1' group by cpf order by cpf desc")) - .getRowCount(), 1995); - testSortDesc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select birth from person where birth != '2011/07/19' group by birth order by birth desc")) - .getRowCount(), 1996); - testSortDesc(resultSet); - - assertEquals((resultSet = connection.executeQuery( - "select name, cpf from person where name = 'name0' and cpf = 'cpf1999' group by name, cpf order by name desc, cpf desc")).getRowCount(), 0); - assertFalse(resultSet.next()); - resultSet.close(); - - assertEquals((resultSet = connection.executeQuery( - "select cpf, name from person where name = 'name1' or cpf = 'cpf1' group by cpf, name order by cpf desc, name desc")).getRowCount(), 2); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth, rowid from person where birth like '2011/0%' group by birth, rowid " - + "order by birth desc, rowid desc")).getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertTrue((count = (resultSet = connection.executeQuery("select birth, rowid from person where rowid > 0 group by rowid, birth " + - "order by rowid desc, birth desc")).getRowCount()) == 1996 || count == 3992); - testSortDesc(resultSet); - - assertEquals((resultSet = connection.executeQuery("select rowid from person where rowid = 1 group by rowid order by rowid desc")) - .getRowCount(), 0); - assertFalse(resultSet.next()); - resultSet.close(); - } - - /** - * Does all tests with empty or all rows deleted. - * - * @param connection The connection with Litebase. - */ - private void testEmptyTable(LitebaseConnection connection) - { - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person add primary key (name)"); - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (cpf)"); - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (birth)"); - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (name, cpf)"); - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (cpf, name)"); - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (birth, rowid)"); - testEmptyOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - testEmptyOrderBy(connection); - } - - /** - * Execute all tests for the created table without repetition. - * - * @param connection The connection with Litebase. - */ - private void executeAllTests(LitebaseConnection connection) - { - testOrderBy(connection); - connection.executeUpdate("alter table person add primary key (name)"); - testOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (cpf)"); - testOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (birth)"); - testOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (name, cpf)"); - testOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (cpf, name)"); - testOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (birth, rowid)"); - testOrderBy(connection); - connection.executeUpdate("alter table person drop primary key"); - testOrderBy(connection); - - connection.executeUpdate("delete from person where name = 'name0' or cpf = 'cpf0' or name = 'name999' or cpf = 'cpf999'"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person add primary key (name)"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (cpf)"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (birth)"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (name, cpf)"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (cpf, name)"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person drop primary key"); - connection.executeUpdate("alter table person add primary key (birth, rowid)"); - testOrderByWithDelete(connection); - connection.executeUpdate("alter table person drop primary key"); - testOrderByWithDelete(connection); - - connection.executeUpdate("delete person where name > 'name0'"); - testEmptyTable(connection); - connection.purge("person"); - testEmptyTable(connection); - } - - /** - * Execute all tests for the created table with repetitions. - * - * @param connection The connection with Litebase. - */ - private void executeAllTestsWithRep(LitebaseConnection connection) - { - testOrderBy(connection); - connection.execute("create index idx on person(name)"); - testOrderBy(connection); - connection.executeUpdate("drop index name on person"); - connection.execute("create index idx on person(cpf)"); - testOrderBy(connection); - connection.executeUpdate("drop index cpf on person"); - connection.execute("create index idx on person(birth)"); - testOrderBy(connection); - connection.executeUpdate("drop index birth on person"); - connection.execute("create index idx on person(name, cpf)"); - testOrderBy(connection); - connection.executeUpdate("drop index name, cpf on person"); - connection.execute("create index idx on person(cpf, name)"); - testOrderBy(connection); - connection.executeUpdate("drop index cpf, name on person"); - connection.execute("create index idx on person(birth, rowid)"); - testOrderBy(connection); - connection.executeUpdate("drop index birth, rowid on person"); - testOrderBy(connection); - - connection.executeUpdate("delete from person where name = 'name0' or cpf = 'cpf0' or name = 'name999' or cpf = 'cpf999'"); - testOrderByWithDelete(connection); - connection.execute("create index idx on person(name)"); - testOrderByWithDelete(connection); - connection.executeUpdate("drop index name on person"); - connection.execute("create index idx on person(cpf)"); - testOrderByWithDelete(connection); - connection.executeUpdate("drop index cpf on person"); - connection.execute("create index idx on person(birth)"); - testOrderByWithDelete(connection); - connection.executeUpdate("drop index birth on person"); - connection.execute("create index idx on person(name, cpf)"); - testOrderByWithDelete(connection); - connection.executeUpdate("drop index name, cpf on person"); - connection.execute("create index idx on person(cpf, name)"); - testOrderByWithDelete(connection); - connection.executeUpdate("drop index cpf, name on person"); - connection.execute("create index idx on person(birth, rowid)"); - testOrderByWithDelete(connection); - connection.executeUpdate("drop index birth, rowid on person"); - testOrderByWithDelete(connection); - - connection.executeUpdate("delete person where name > 'name0'"); - testEmptyTable(connection); - connection.purge("person"); - testEmptyTable(connection); - } - - /** - * Tests ascending sort results. - * - * @param resultSet The result of ascending sorts. - */ - private void testSortAsc(ResultSet resultSet) - { - resultSet.next(); - - ResultSetMetaData rsMD = resultSet.getResultSetMetaData(); - - if (rsMD.getColumnType(1) == ResultSetMetaData.INT_TYPE) - { - int first = resultSet.getInt(1), - last; - while (resultSet.next()) - { - last = resultSet.getInt(1); - assertLowerOrEqual(first, last); - first = last; - } - } - else - { - String first = resultSet.getString(1), - last; - while (resultSet.next()) - { - last = resultSet.getString(1); - assertLowerOrEqual(first.compareTo(last), 0); - first = last; - } - } - resultSet.close(); - } - - /** - * Tests descending sort results. - * - * @param resultSet The result of descending sorts. - */ - private void testSortDesc(ResultSet resultSet) - { - resultSet.next(); - - ResultSetMetaData rsMD = resultSet.getResultSetMetaData(); - - if (rsMD.getColumnType(1) == ResultSetMetaData.INT_TYPE) - { - int first = resultSet.getInt(1), - last; - while (resultSet.next()) - { - last = resultSet.getInt(1); - assertGreaterOrEqual(first, last); - first = last; - } - } - else - { - String first = resultSet.getString(1), - last; - while (resultSet.next()) - { - last = resultSet.getString(1); - assertGreaterOrEqual(first.compareTo(last), 0); - first = last; - } - } - resultSet.close(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestPreparedStatement.java b/LitebaseSDK/src/java/samples/sys/testcases/TestPreparedStatement.java deleted file mode 100644 index 7111717aa9..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestPreparedStatement.java +++ /dev/null @@ -1,718 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.*; -import totalcross.unit.TestCase; -import totalcross.util.Date; - -/** - * Tests the use of prepared statements. - */ -public class TestPreparedStatement extends TestCase -{ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - if (!driver.exists("Lancamento")) - { - driver.execute("create table Lancamento(data int, ndoc int, valor double, pago short, tipo short, dataExt char(10), origem char(20), " - + "obs char(50) nocase)"); - driver.execute("CREATE INDEX IDX_0 ON Lancamento(rowid)"); - } - else - try - { - driver.executeUpdate("delete from lancamento"); - } - catch (DriverException exception) - { - assertTrue(exception.getMessage().startsWith("It is not possible to open a table within a connection with a different")); - driver.executeUpdate("drop table lancamento"); - driver.execute("create table Lancamento(data int, ndoc int, valor double, pago short, tipo short, dataExt char(10), origem char(20), " - + "obs char(50) nocase)"); - } - - PreparedStatement psListar = driver.prepareStatement("select rowid,pago,dataExt,ndoc,valor,tipo from lancamento where data <= ? and " - + "(data >= ? or pago=0) order by data"); - assertEquals(1, driver.executeUpdate("insert into lancamento (data,dataext,ndoc,valor,tipo,origem,obs,pago) values " - + "(20051219,'19/12/2005',0000004,-444.00,0,'Alimentacao','',0)")); - psListar.setInt(0, 20051231); - psListar.setInt(1, 20051130); - assertEquals(1, executePreparedQuery(psListar)); - assertEquals(1, driver.executeUpdate("insert into lancamento (data,dataext,ndoc,valor,tipo,origem,obs,pago) values " - + "(20051220,'20/12/2005',0000088,-34.00,2,'Gas de Cozinha','',0)")); - assertEquals(2, executePreparedQuery(psListar)); - - try // A null can't be inserted in a prepared statement. - { - psListar.setNull(0); - fail("1"); - } - catch (SQLParseException exception) {} - - try // A null can't be inserted in a prepared statement. - { - psListar.setString(0, null); - fail("2"); - } - catch (SQLParseException exception) {} - - assertTrue(psListar.isValid()); - psListar.close(); - assertFalse(psListar.isValid()); - - try - { - driver.executeUpdate("drop table PERSON"); - } - catch (DriverException exception) {} // Table not found. - - // Creates thr table. - driver.execute("create table PERSON (FIRST_NAME CHAR(30), LAST_NAME CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, YEARS_EXP_JAVA INT, " - + "YEARS_EXP_C INT )"); - - // Creates the insert prepared statement. - PreparedStatement preparedStmt = driver.prepareStatement("insert into PERSON values (?, ?, ?, ?, ?, ?) "); // added space at the end. - - // Inserts the rows. - // Row 1 - preparedStmt.setString(0, "guilherme"); - preparedStmt.setString(1, "hazan"); - preparedStmt.setDouble(2, 4400.50); - preparedStmt.setDouble(3, 3400.80); - preparedStmt.setInt(4, 10); - preparedStmt.setInt(5, 6); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 2 - preparedStmt.setString(0, "raimundo"); - preparedStmt.setString(1, "correa"); - preparedStmt.setDouble(2, 3400.50); - preparedStmt.setDouble(3, 3400.50); - preparedStmt.setInt(4, 11); - preparedStmt.setInt(5, 26); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 3 - preparedStmt.setString(0, "ricardo"); - preparedStmt.setString(1, "zorba"); - preparedStmt.setDouble(2, 10400.50); - preparedStmt.setDouble(3, 5000.20); - preparedStmt.setInt(4, 23); - preparedStmt.setInt(5, 23); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 4 - preparedStmt.setString(0, "cher"); - preparedStmt.setString(1, "cher"); - preparedStmt.setDouble(2, 1000.50); - preparedStmt.setDouble(3, 3400.50); - preparedStmt.setInt(4, 4); - preparedStmt.setInt(5, 3); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 5 - preparedStmt.setString(0, "lero"); - preparedStmt.setString(1, "lero"); - preparedStmt.setDouble(2, 2001.34); - preparedStmt.setDouble(3, 1000.35); - preparedStmt.setInt(4, 2); - preparedStmt.setInt(5, 6); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 6 - preparedStmt.setString(0, "zico"); - preparedStmt.setString(1, "mengao"); - preparedStmt.setDouble(2, 1000.51); - preparedStmt.setDouble(3, 1000.51); - preparedStmt.setInt(4, 12); - preparedStmt.setInt(5, 0); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 7 - preparedStmt.setString(0, "roberto"); - preparedStmt.setString(1, "dinamite"); - preparedStmt.setDouble(2, 2222.51); - preparedStmt.setDouble(3, 1.21); - preparedStmt.setInt(4, 10); - preparedStmt.setInt(5, 10); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 8 - preparedStmt.setString(0, "socrates"); - preparedStmt.setString(1, "sampaio"); - preparedStmt.setDouble(2, 1111.50); - preparedStmt.setDouble(3, 1111.50); - preparedStmt.setInt(4, 7); - preparedStmt.setInt(5, 11); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 9 - preparedStmt.setString(0, "paulo"); - preparedStmt.setString(1, "falcao"); - preparedStmt.setDouble(2, 2800.04); - preparedStmt.setDouble(3, 1.21); - preparedStmt.setInt(4, 15); - preparedStmt.setInt(5, 12); - assertEquals(1, preparedStmt.executeUpdate()); - - // Row 10 - preparedStmt.setString(0, "leo"); - preparedStmt.setString(1, "junior"); - preparedStmt.setDouble(2, 2.50); - preparedStmt.setDouble(3, 3400.50); - preparedStmt.setInt(4, 4); - preparedStmt.setInt(5, 5); - assertEquals(1, preparedStmt.executeUpdate()); - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Run SQL queries to test the insert statements. - assertEquals(10, executeQuery(driver, "select * from PERSON")); // No where clause - - // With where clause. - assertEquals(2, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur and last_name like '%or%'")); - assertEquals(9, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur or last_name like '%or%'")); - assertEquals(4, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur and not years_exp_c > 10")); - assertEquals(6, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 and years_exp_java >= 10) or FIRST_NAME > 'r'")); - assertEquals(4, executeQuery(driver, "select * from PERSON where salary_prev < 3000 and (years_exp_java >= 10 or FIRST_NAME > 'r')")); - assertEquals(2, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 or not years_exp_c >= years_exp_java) and not " - + "(years_exp_java >= 10 or FIRST_NAME > 'r')")); - assertEquals(8, executeQuery(driver, "select * from PERSON where salary_prev < 3000 or ((not years_exp_c >= years_exp_java) " - + "and not years_exp_java >= 10) or FIRST_NAME > 'r'")); - - // Run SQL update command to test the insert statements. - assertEquals(8, driver.executeUpdate("update PERSON set FIRST_NAME = 'x' where salary_prev < 3000 or ((not years_exp_c >= years_exp_java) " - + "and not years_exp_java >= 10) or FIRST_NAME > 'r'")); - - // Tests SELECT prepared statements. - preparedStmt = driver.prepareStatement("select * from PERSON where salary_prev < ? or ((not years_exp_c >= years_exp_java) " - + "and not years_exp_java >= ?) or FIRST_NAME > ?"); - - preparedStmt.setDouble(0, 3000); - preparedStmt.setInt(1, 10); - preparedStmt.setString(2, "r"); - assertEquals(8, executePreparedQuery(preparedStmt)); - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Tests SELECT with WHERE and HAVING. - (preparedStmt = driver.prepareStatement("select years_exp_java, count(*) as total from person where years_exp_c > ? and last_name < ? group by " - + "years_exp_java having total = ?")).setInt(0, 1); - preparedStmt.setString(1, "t"); - preparedStmt.setInt(2, 1); - assertEquals(4, executePreparedQuery(preparedStmt)); - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Tests UPDATE prepared statements. - (preparedStmt = driver.prepareStatement("update PERSON set FIRST_NAME = ?, last_name = ? where salary_prev < ? or " - + "((not years_exp_c >= years_exp_java) and not years_exp_java >= ?) or FIRST_NAME not like ?")).setString(0, "roberto"); - preparedStmt.setString(1, "carlos"); - preparedStmt.setDouble(2, 3000); - preparedStmt.setInt(3, 10); - preparedStmt.setString(4, "g%"); - assertEquals(9, preparedStmt.executeUpdate()); - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Checks if the UPDATE worked. - assertEquals(9, executeQuery(driver, "select * from PERSON where first_name = 'roberto' and last_name = 'carlos'")); - - // Tests DELETE prepared statements. - (preparedStmt = driver.prepareStatement("delete person where salary_prev <= ? and not years_exp_c > ?")).setDouble(0, 4000); - preparedStmt.setInt(1, 10); - assertEquals(6, preparedStmt.executeUpdate()); - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - assertEquals(4, executeQuery(driver, "select * from PERSON")); // Checks if DELETE worked. - - // Tests exceptions. - - // Parameter values not defined. - (preparedStmt = driver.prepareStatement("select years_exp_java, count(*) as total from person where years_exp_c > ? and last_name < ? group by " - + "years_exp_java having total = ?")).setInt(0, 1); - preparedStmt.setInt(2, 1); - - try - { - assertEquals(4, executePreparedQuery(preparedStmt)); - fail("3"); - } - catch (DriverException exception) - { - assertGreaterOrEqual(exception.getMessage().indexOf("had their values defined"), 0); - } - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Incompatible types. - preparedStmt = driver.prepareStatement("select years_exp_java, count(*) as total from person where years_exp_c > ? and last_name < ? group by " - + "years_exp_java having total = ?"); - - try - { - preparedStmt.setInt(1, 1); - fail("4"); - } - catch (DriverException exception) {} - try - { - preparedStmt.setBlob(0, null); - fail("5"); - } - catch (SQLParseException exception) {} - try - { - preparedStmt.setString(0, "a"); - fail("6"); - } - catch (SQLParseException exception) {} - try - { - preparedStmt.setDouble(0, 1); - fail("7"); - } - catch (DriverException exception) {} - try - { - preparedStmt.setDate(0, new Date()); - fail("8"); - } - catch (SQLParseException exception) {} - try - { - preparedStmt.setDateTime(0, new Time()); - fail("9"); - } - catch (SQLParseException exception) {} - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Tests when there are no where clauses in the prepared statement. - if (driver.exists("PEDIDO")) - driver.executeUpdate("drop table pedido"); - driver.execute("create table PEDIDO(NUMERO int primary key, TOTAL double)"); - driver.executeUpdate("insert into pedido values (10,1000.54321)"); - driver.executeUpdate("insert into pedido values (11,2000)"); - driver.executeUpdate("insert into pedido values (12,3000)"); - - ResultSet rs = driver.prepareStatement("select sum(TOTAL) as VALORTOTAL from PEDIDO").executeQuery(); - assertTrue(rs.first()); - rs.setDecimalPlaces(1, 4); - assertEquals("6000.5432", rs.getString(1)); - rs.close(); - - // Tests prepared statement reutilization for updates. - if (!driver.exists("ITEMPEDIDO")) - { - driver.execute("create table ITEMPEDIDO(PEDIDO int, PRODUTO int, QTDE int, UNITARIO double, TOTAL double)"); - driver.execute("create index IDX_ITEMPEDIDO_PEDIDO on ITEMPEDIDO(PEDIDO)"); - } - else - try - { - driver.executeUpdate("delete itempedido"); - } - catch (DriverException exception) - { - assertTrue(exception.getMessage().startsWith("It is not possible to open a table within a connection with a different")); - driver.executeUpdate("drop table itempedido"); - driver.execute("create table ITEMPEDIDO(PEDIDO int, PRODUTO int, QTDE int, UNITARIO double, TOTAL double)"); - driver.execute("create index IDX_ITEMPEDIDO_PEDIDO on ITEMPEDIDO(PEDIDO)"); - } - - PreparedStatement psInsert = driver.prepareStatement("insert into ITEMPEDIDO(PEDIDO, PRODUTO, QTDE, UNITARIO, TOTAL) values(?, ?, ?, ?, ?)"); - - // Inserts 3 orders with 4 items. - int i = 4, - j; - - while (--i >= 1) - { - j = 5; - while (--j >= 1) - { - psInsert.clearParameters(); - psInsert.setInt(0, i); - psInsert.setInt(1, j); - psInsert.setInt(2, 1); - psInsert.setDouble(3, 1); - psInsert.setDouble(4, 1); - assertEquals(1, psInsert.executeUpdate()); - } - } - - PreparedStatement psUpdate = driver.prepareStatement("update ITEMPEDIDO set QTDE = ?, UNITARIO = ?, TOTAL = ? where PEDIDO = ? and " - + "PRODUTO = ?"); - // Updates the first item of the first order. - psUpdate.clearParameters(); - psUpdate.setInt(0, 2); - psUpdate.setDouble(1, 1); - psUpdate.setDouble(2, 2); - psUpdate.setInt(3, 1); - psUpdate.setInt(4, 1); - assertEquals(1, psUpdate.executeUpdate()); - - // Updates the second item of the first order. - psUpdate.clearParameters(); - psUpdate.setInt(0, 2); - psUpdate.setDouble(1, 1); - psUpdate.setDouble(2, 2); - psUpdate.setInt(3, 1); - psUpdate.setInt(4, 2); - assertEquals(1, psUpdate.executeUpdate()); - - assertTrue(psInsert.isValid()); - assertTrue(psUpdate.isValid()); - psInsert.close(); - psUpdate.close(); - assertFalse(psInsert.isValid()); - assertFalse(psUpdate.isValid()); - - // Tests shorts and floats using prepared statement. - if (driver.exists("teste")) - driver.executeUpdate("drop table teste"); - driver.execute("create table teste(id int primary key, sh1 short, x float)"); - - (preparedStmt = driver.prepareStatement("insert into teste values (?, ?, ?)")).setInt(0, 3); - preparedStmt.setShort(1, (short)5); - preparedStmt.setFloat(2, 7); - preparedStmt.executeUpdate(); - (rs = driver.executeQuery("select * from teste")).first(); - assertEquals(3, rs.getInt(1)); - assertEquals(5, rs.getShort(2)); - assertEquals(7, rs.getFloat(3), 0.001); - rs.close(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - (preparedStmt = driver.prepareStatement("update teste set sh1 = ?, x = ?")).setShort(0, (short)6); - preparedStmt.setFloat(1, 8); - preparedStmt.executeUpdate(); - (rs = driver.executeQuery("select * from teste")).first(); - assertEquals(3, rs.getInt(1)); - assertEquals(6, rs.getShort(2)); - assertEquals(8, rs.getFloat(3), 0.001); - rs.close(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - (preparedStmt = driver.prepareStatement("select * from teste where sh1 = ? and x = ?")).setShort(0, (short)6); - preparedStmt.setFloat(1, 8); - (rs = preparedStmt.executeQuery()).first(); - assertEquals(3, rs.getInt(1)); - assertEquals(6, rs.getShort(2)); // o valor lido � sempre 0!! - assertEquals(8, rs.getFloat(3), 0.001); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - rs.close(); - - (preparedStmt = driver.prepareStatement("delete from teste where sh1 = ? and x = ?")).setShort(0, (short)6); - preparedStmt.setFloat(1, 8); - preparedStmt.executeUpdate(); - rs = driver.executeQuery("select * from teste"); - rs.first(); - assertEquals(0, rs.getRowCount()); - rs.close(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - if (driver.exists("teste2")) - driver.executeUpdate("drop table teste2"); - driver.execute("create table teste2 (id2 int)"); - - (preparedStmt = driver.prepareStatement("select * from teste, teste2 where id = id2 and sh1 = ? and x = ?")).setShort(0, (short)6); - preparedStmt.setFloat(1, 8); - - // Tests a select prepared statement with group by. - if (driver.exists("table1")) - driver.executeUpdate("drop table table1"); - driver.execute("create table table1 (field1 int NOT NULL, field2 char(50) NOT NULL, primary key (field1))"); - preparedStmt = driver.prepareStatement("insert into table1 values(?, 'Thiago')"); - i = -1; - while (++i < 5) - { - preparedStmt.setInt(0, i + 1); - preparedStmt.executeUpdate(); - } - rs = (preparedStmt = driver.prepareStatement("select field2 from table1 group by field2")).executeQuery(); - assertTrue(rs.next()); - assertEquals("Thiago", rs.getString(1)); - assertFalse(rs.next()); - rs.close(); - rs = preparedStmt.executeQuery(); - assertTrue(rs.next()); - assertEquals("Thiago", rs.getString(1)); - assertFalse(rs.next()); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - rs.close(); - - // Tests a select prepared statement with where clause and indices. - if (driver.exists("teste")) - driver.executeUpdate("drop table teste"); - driver.execute("create table teste (idPessoa int primary key, Nome varchar(50), TipoOp short)"); - driver.executeUpdate("insert into teste values (-2, 'joao', 0)"); - preparedStmt = driver.prepareStatement("select * from teste where ( (1 != 1) or (Upper(Nome) = ? and 1 = 1)) and (TipoOp < 3 and idPessoa != -2)"); - preparedStmt.setString(0, "JOAO"); - assertFalse((rs = preparedStmt.executeQuery()).first()); - rs.close(); - preparedStmt.setString(0, "JOAO"); - - try // Invalid index. - { - preparedStmt.setString(2, "JOAO"); - fail("10"); - } - catch (DriverException exception) {} - - assertFalse((rs = preparedStmt.executeQuery()).first()); - rs.close(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Tests a select prepared statement without parameters and like. - if (driver.exists("property")) - driver.executeUpdate("drop table property"); - driver.execute("create table property (keyP char(100) NOT NULL, value char(200), descr char(100) NOT NULL, status int NOT NULL, " - + "primary key (keyP))"); - driver.executeUpdate("insert into property (keyP, value, descr, status) values ('ws_prod', NULL, 'Produ��o', 1)"); - driver.executeUpdate("insert into property (keyP, value, descr, status) values ('ws_test', NULL, 'Teste', 2)"); - driver.executeUpdate("insert into property (keyP, value, descr, status) values ('tables_bkp', 'true', 'Criar c�pia de seguran�a', 1)"); - driver.executeUpdate("insert into property (keyP, value, descr, status) values ('tables_drop', NULL, 'Excluir tabelas', 3)"); - driver.executeUpdate("insert into property (keyP, value, descr, status) values ('tables_restore', NULL, 'Recuperar tabelas', 3)"); - - assertTrue((rs = (preparedStmt = driver.prepareStatement("select property.value from property where keyP like 'ws%'")).executeQuery()).next()); - rs.close(); - - // Invalid indices. - try - { - preparedStmt.setInt(0, 0); - fail("11"); - } - catch (DriverException exception) {} - try - { - preparedStmt.setInt(0, -1); - fail("12"); - } - catch (DriverException exception) {} - - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - preparedStmt = driver.prepareStatement("select * from teste, teste2"); - - // Tests what happens if a table being used by a prepared statement is dropped. - driver.executeUpdate("drop table teste"); - try - { - preparedStmt.executeQuery(); - fail("13"); - } - catch (IllegalStateException exception) {} - catch (DriverException exception) {} - driver.execute("create table teste(id int primary key, sh1 short, x float)"); - try - { - preparedStmt.executeQuery(); - } - catch (DriverException exception) {} - catch (IllegalStateException exception) {} - - preparedStmt = driver.prepareStatement("select * from teste, teste2 where id = id2 and sh1 = ? and x = ?"); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - // Tests update prepared statements with some data to be changed already given. - if (driver.exists("rota")) - driver.executeUpdate("DROP TABLE rota"); - driver.execute("CREATE TABLE rota (id_rota_dia CHAR(1), ds_rota CHAR(20) NOT NULL, cd_rota INT NOT NULL, dt_abertura DATE, PRIMARY KEY(cd_rota))"); - driver.executeUpdate("INSERT INTO rota(ds_rota, cd_rota) VALUES ('Rota 1', 1)"); - driver.executeUpdate("INSERT INTO rota(ds_rota, cd_rota) VALUES ('Rota 2', 2)"); - driver.executeUpdate("INSERT INTO rota(ds_rota, cd_rota) VALUES ('Rota 3', 3)"); - driver.executeUpdate("INSERT INTO rota(ds_rota, cd_rota) VALUES ('Rota 4', 4)"); - - Date date = new Date(); - String dateStr = date.toString(); - - (preparedStmt = driver.prepareStatement("UPDATE rota SET dt_abertura = ?, id_rota_dia = 'A' WHERE cd_rota = ?")).clearParameters(); - preparedStmt.setDate(0, date); - preparedStmt.setInt(1, 2); - preparedStmt.executeUpdate(); - (rs = driver.executeQuery("select dt_abertura from rota where cd_rota = 2")).first(); - assertEquals(1, rs.getRowCount()); - assertEquals(dateStr, rs.getDate(1).toString()); - rs.close(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - (preparedStmt = driver.prepareStatement("UPDATE rota SET id_rota_dia = 'A', dt_abertura = ? WHERE cd_rota = ?")).clearParameters(); - preparedStmt.setDate(0, date); - preparedStmt.setInt(1, 1); - preparedStmt.executeUpdate(); - (rs = driver.executeQuery("select dt_abertura from rota where cd_rota = 1")).first(); - assertEquals(1, rs.getRowCount()); - assertEquals(dateStr, rs.getDate(1).toString()); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - rs.close(); - - preparedStmt = null; - Vm.gc(); - - // Tests toString(). - if (driver.exists("exemplo")) - driver.executeUpdate("drop table exemplo"); - driver.execute("CREATE TABLE exemplo(campo1 INT, campo2 CHAR(50), PRIMARY KEY(campo1))"); - - (preparedStmt = driver.prepareStatement("INSERT INTO exemplo(campo1, campo2) VALUES (?, ?)")).toString(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - (preparedStmt = driver.prepareStatement("INSERT INTO exemplo(campo1, campo2) VALUES (?, ?)")).toString(); - assertTrue(preparedStmt.isValid()); - preparedStmt.close(); - assertFalse(preparedStmt.isValid()); - - try // executeUpdate() with a select. - { - driver.prepareStatement("select * from teste").executeUpdate(); - fail("14"); - } - catch (DriverException exception) {} - - // executeQuery() without a select. - try - { - driver.prepareStatement("create table teste2 (id2 int)").executeQuery(); - fail("15"); - } - catch (DriverException exception) {} - try - { - driver.prepareStatement("create index idx on teste2(id2)").executeQuery(); - fail("16"); - } - catch (DriverException exception) {} - try - { - driver.prepareStatement("insert into teste2 values (0)").executeQuery(); - fail("17"); - } - catch (DriverException exception) {} - try - { - driver.prepareStatement("delete from teste2").executeQuery(); - fail("18"); - } - catch (DriverException exception) {} - try - { - driver.prepareStatement("update teste2 set id2 = 1").executeQuery(); - fail("19"); - } - catch (DriverException exception) {} - try - { - driver.prepareStatement("alter table teste2 drop primary key").executeQuery(); - fail("20"); - } - catch (DriverException exception) {} - - try - { - psInsert.close(); - fail("21"); - } - catch (IllegalStateException exception) {} - try - { - psUpdate.close(); - fail("22"); - } - catch (IllegalStateException exception) {} - try - { - psListar.close(); - fail("23"); - } - catch (IllegalStateException exception) {} - - driver.closeAll(); - - try - { - preparedStmt.close(); - fail("24"); - } - catch (IllegalStateException exception) {} - } - - /** - * Execute a query and returns the number of rows that answers the query. - * - * @param driver The connection with Litebase. - * @param sql The query to be executed. - * @return The total number of rows that answers the query. - */ - private int executeQuery(LitebaseConnection driver, String sql) - { - int count = 0; - ResultSet rs = driver.executeQuery(sql); - while (rs.next()) - count++; - rs.close(); - return count; - } - - /** - * Execute a prepated query and returns the number of rows that answers the query. - * - * @param stmt A prepared query. - * @return The total number of rows that answers the query. - */ - private int executePreparedQuery(PreparedStatement stmt) - { - int count = 0; - ResultSet rs = stmt.executeQuery(); - while (rs.next()) - count++; - rs.close(); - return count; - } - -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestPrimaryKey.java b/LitebaseSDK/src/java/samples/sys/testcases/TestPrimaryKey.java deleted file mode 100644 index a618a6fbb9..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestPrimaryKey.java +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests Litebase primary key. - */ -public class TestPrimaryKey extends TestCase -{ - LitebaseConnection driver = AllTests.getInstance("Test"); - String[] insertRows = // Rows to insert. - { - "insert into PERSON_PK values ('guilherme', 'Rio de Janeiro', 4400.50, 3400.80, 10, 6)", - "insert into PERSON_PK values ('raimundo', 'Fortaleza', 3400.50, 3400.50, 11, 26)", - "insert into PERSON_PK values ('ricardo', 'Natal', 10400.50, 5000.20, 23, 23)", - "insert into PERSON_PK values ('cher', 'Fortaleza', 1000.50, 3400.50, 4, 3)", - "insert into PERSON_PK values ('maria', 'Paraty', 2001.34, 1000.35, 2, 6)", - "insert into PERSON_PK values ('zico', 'Ouro Preto', 1000.51, 1000.51, 12, 0)", - "insert into PERSON_PK values ('roberto', 'Rio de Janeiro', 2222.51, 1.21, 10, 10)", - "insert into PERSON_PK values ('socrates', 'Porto Seguro', 1111.50, 1111.50, 7, 11)", - "insert into PERSON_PK values ('paulo', 'Rio de Janeiro', 2800.04, 1.21, 15, 12)", - "insert into PERSON_PK values ('leo', 'Natal', 2.50, 3400.50, 4, 5)", - "insert into PERSON_PK values ('maria', 'Foz do Igua�u', 4400.50, 3400.80, 10, 6)", - "insert into PERSON_PK values ('guilherme', 'Porto Seguro', 3400.50, 3400.50, 11, 26)", - "insert into PERSON_PK values ('zanata', 'Florianopolis', 10400.50, 5000.20, 23, 23)", - "insert into PERSON_PK values ('roberto', 'Natal', 1000.50, 3400.50, 4, 3)", - "insert into PERSON_PK values ('maria', 'Fortaleza', 2001.34, 1000.35, 2, 6)", - "insert into PERSON_PK values ('maria', 'Porto Seguro', 1000.51, 1000.51, 12, 0)", - "insert into PERSON_PK values ('roberto', 'Ouro Preto', 2222.51, 1.21, 10, 10)", - "insert into PERSON_PK values ('paulo', 'Porto Seguro', 1111.50, 1111.50, 7, 11)", - "insert into PERSON_PK values ('cher', 'Rio de Janeiro', 2800.04, 1.21, 15, 12)", - "insert into PERSON_PK values ('maria', 'Rio de Janeiro', 2.50, 3400.50, 4, 5)" - }; - int numRows = insertRows.length; - - /** - * The main test method. - */ - public void testRun() - { - try - { - driver.executeUpdate("drop table PERSON_PK"); - } - catch (DriverException exception) {} // Table not found. - - try // Tries to create a table with 2 primary keys. - { - driver.execute("create table PERSON_PK (FIRST_NAME CHAR(30) PRIMARY KEY, CITY CHAR(30), SALARY_CUR DOUBLE PRIMARY KEY, SALARY_PREV DOUBLE, " - + "YEARS_EXP_JAVA INT, YEARS_EXP_C INT )"); - fail("1"); - } - catch (SQLParseException exception) {} - - // Creates table with the first name being the primary key. - test_person_PK("create table PERSON_PK (FIRST_NAME CHAR(30) PRIMARY KEY, CITY CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, " - + "YEARS_EXP_JAVA INT, YEARS_EXP_C INT )"); - test_person_PK("create table PERSON_PK (FIRST_NAME CHAR(30), CITY CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, YEARS_EXP_JAVA INT, " - + "YEARS_EXP_C INT, PRIMARY KEY(FIRST_NAME))"); - - output("Testing updating the same key"); - if (driver.exists("tabps")) - driver.executeUpdate("drop table tabps"); - driver.execute("create table tabps (idade int primary key, name char(30))"); - driver.executeUpdate("insert into tabps values (5, 'cacule')"); - driver.executeUpdate("insert into tabps values (6, 'vera')"); - assertEquals(1, driver.executeUpdate("update tabps set idade = 5, name = 'michelle' where idade = 5")); // guich@564_18 - - // Tests that prepared statement won't create a duplicate primary key. - PreparedStatement ps = driver.prepareStatement("insert into tabps values (?, ?)"); - ps.setString(0, "1"); - ps.setString(1, "1"); - ps.executeUpdate(); - ps.setInt(0, 2); - ps.setString(1, "2"); - ps.executeUpdate(); - ResultSet resultSet = driver.executeQuery("select idade from tabps where name = '1' or name = '2'"); - resultSet.next(); - assertEquals(1, resultSet.getInt(1)); - resultSet.next(); - assertEquals(2, resultSet.getInt(1)); - resultSet.close(); - - try // Repeated primary key. - { - driver.executeUpdate("update tabps set idade = 5, name = 'michelle' where idade = 6"); // guich@564_19 - fail("2"); - } - catch (PrimaryKeyViolationException exception) {} - - driver.executeUpdate("drop table tabps"); - driver.execute("create table tabps (x int)"); - driver.execute("create index idx on tabps(x)"); - driver.execute("create index idx on tabps(x, rowid)"); - - // It is not possible to add a primary key to a column that has already an index or columns that already compose an index in the same order of - // the key columns. - try - { - driver.executeUpdate("alter table tabps add primary key (x)"); - fail("3"); - } - catch (AlreadyCreatedException exception) {} - try - { - driver.executeUpdate("alter table tabps add primary key (x, rowid)"); - fail("4"); - } - catch (AlreadyCreatedException exception) {} - - driver.executeUpdate("alter table tabps add primary key (rowid, x)"); // This must work. - driver.closeAll(); - } - - /** - * Tests a query. - * - * @param sql The query to be executed - * - * @return The number of rows returned by the query. - */ - private int executeQuery(String sql) - { - int count = 0; - output("\n" + sql); - ResultSet rs = driver.executeQuery(sql); - while (rs.next()) - { - output(rs.getString("FIRST_NAME") + ", " + rs.getString("city")); - count++; - } - - rs.close(); - return count; - } - - /** - * Does tests with primary key. - * - * @param sql The create table sql string. - */ - private void test_person_PK(String sql) - { - if (driver.exists("person_pk")) - driver.executeUpdate("drop table person_pk"); - driver.execute(sql); - - // Inserts rows. It should raise the exception in the 11th insert. - int i = -1; - - try - { - while (++i < numRows) - driver.executeUpdate(insertRows[i]); - fail("5"); - } - catch (PrimaryKeyViolationException exception) {} - - assertEquals(i, 10); - - try // Tries to add another primary key. - { - driver.executeUpdate("alter table PERSON_PK add primary key(city)"); - fail("6"); - } - catch (AlreadyCreatedException exception) {} - - driver.executeUpdate("alter table PERSON_PK drop primary key"); // Drops the existing primary key. - - i = 9; - while (++i < numRows) // Inserts the remaining rows. - assertEquals(1, driver.executeUpdate(insertRows[i])); - - try // Tries to drop the key again. - { - driver.executeUpdate("alter table person_pk drop primary key"); - fail("7"); - } - catch (DriverException exception) {} - - try // Tries to add the primary key again. It must raise an exception, since now there are duplicated records. - { - driver.executeUpdate("alter table person_pk add primary key(first_name)"); - fail("8"); - } catch (PrimaryKeyViolationException exception) {} - - try // Tries again. - { - driver.executeUpdate("alter table person_pk add primary key(first_name)"); - fail("9"); - } - catch (PrimaryKeyViolationException exception) {} - - // Tries again after closing the driver. - driver.closeAll(); - driver = AllTests.getInstance("Test"); - try - { - driver.executeUpdate("alter table person_pk add primary key(first_name)"); - fail("10"); - } - catch (PrimaryKeyViolationException exception) {} - - // Deletes the duplicated records. - assertEquals(2, driver.executeUpdate("delete person_pk where first_name = 'guilherme'")); - assertEquals(5, driver.executeUpdate("delete person_pk where first_name = 'maria'")); - assertEquals(2, driver.executeUpdate("delete person_pk where first_name = 'paulo'")); - assertEquals(3, driver.executeUpdate("delete person_pk where first_name = 'roberto'")); - assertEquals(2, driver.executeUpdate("delete person_pk where first_name = 'cher'")); - - assertEquals(6, executeQuery("select * from person_pk")); - assertEquals(6, executeQuery("select * from person_pk where 1 = 1")); - - // Tries one more time to add the primary key again. It should work, since there are no duplicated records anymore. - driver.executeUpdate("alter table person_pk add primary key(first_name)"); - - // Checks INSERT statements. The first should pass; the second should fail. - driver.executeUpdate("insert into person_pk values ('maria', 'Rio de Janeiro', 2.50, 3400.50, 4, 5)"); - try - { - driver.executeUpdate("insert into person_pk values ('maria', 'Rio de Janeiro', 2.50, 3400.50, 4, 5)"); - fail("11"); - } - catch (PrimaryKeyViolationException exception) {} - - // Checks UPDATE statements. The first two should pass; the third should fail. - assertEquals(1, driver.executeUpdate("update person_pk set first_name = 'madalena' where first_name = 'maria'")); - assertEquals(1, driver.executeUpdate("update person_pk set city = 'new york' where first_name = 'madalena'")); - try - { - driver.executeUpdate("update person_pk set first_name = 'zanata' where first_name = 'madalena'"); - fail("12"); - } - catch (PrimaryKeyViolationException exception) {} - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestReIndex2rowId.java b/LitebaseSDK/src/java/samples/sys/testcases/TestReIndex2rowId.java deleted file mode 100644 index ab134a948a..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestReIndex2rowId.java +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests index on rowid after table creation. - */ -public class TestReIndex2rowId extends TestCase -{ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - /** - * The main test method. - */ - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - - // Creates and populates the table. - driver.execute("create table person(name char(20), age int)"); - driver.executeUpdate("insert into person(name, age) values ('RLN_0', 0)"); - driver.executeUpdate("insert into person(name, age) values ('RLN_1', 1)"); - driver.executeUpdate("insert into person(name, age) values ('RLN_2', 2)"); - driver.executeUpdate("insert into person(name, age) values ('RLN_3', 3)"); - driver.executeUpdate("insert into person(name, age) values ('RLN_4', 4)"); - - driver.execute("create index idxId on person(rowId)"); // Creates an index on rowid after some inserts. - - // Do selects. - ResultSet rs = driver.executeQuery("Select rowid, name, age from person where rowid = 3"); - assertEquals(1, rs.getRowCount()); - assertTrue(rs.next()); - assertEquals(3, rs.getInt("rowid")); - assertEquals("RLN_2", rs.getString("name")); - assertEquals(2, rs.getInt("age")); - rs.close(); - - assertEquals(3, (rs = driver.executeQuery("Select rowid, name, age from person where rowid > 2")).getRowCount()); - assertTrue(rs.next()); - assertEquals(3, rs.getInt("rowid")); - assertEquals("RLN_2", rs.getString("name")); - assertEquals(2, rs.getInt("age")); - assertTrue(rs.next()); - assertEquals(4, rs.getInt("rowid")); - assertEquals("RLN_3", rs.getString("name")); - assertEquals(3, rs.getInt("age")); - assertTrue(rs.next()); - assertEquals(5, rs.getInt("rowid")); - assertEquals("RLN_4", rs.getString("name")); - assertEquals(4, rs.getInt("age")); - rs.close(); - - assertEquals(6, driver.getCurrentRowId("person")); - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestRename.java b/LitebaseSDK/src/java/samples/sys/testcases/TestRename.java deleted file mode 100644 index cc1d3056fa..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestRename.java +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.unit.*; - -/** - * This test verifies if the "rename table" and "rename column" are working properly. - */ -public class TestRename extends TestCase -{ - LitebaseConnection driver; - - /** - * The main test method. - */ - public void testRun() - { - driver = AllTests.getInstance("Test"); - - // Drops the tables if they exist. - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - if (driver.exists("person2")) - driver.executeUpdate("drop table person2"); - - // Creates and populates the table with one row. - driver.execute("create table person(name char(16) , age int, primary key(name, age))"); - driver.execute("CREATE index ageindex on person(age)"); - driver.executeUpdate("insert into person values ('renato novais', 12)"); - driver.executeUpdate("insert into person values ('fabio novais', 12)"); - - driver.closeAll(); - driver = AllTests.getInstance("Test"); - - assertEquals(2, selectOldTable()); // Does a select. - - try // The index already exists. - { - output("Creating index on old table"); - driver.execute("CREATE index ageindex on person(age)"); - fail("1"); - } - catch (AlreadyCreatedException e) {} - - try // Tries to rename unknown table. - { - driver.executeUpdate("ALTER TABLE peRson2 RENAME to person "); - fail("2"); - } - catch (DriverException exception) {} - - try // Tries to add a primary key to an unknown table. - { - driver.executeUpdate("ALTER TABLE peRson2 add primary key (age) "); - fail("3"); - } - catch (DriverException exception) {} - - try // Tries to drop a primary key from an unknown table. - { - driver.executeUpdate("ALTER TABLE person2 drop primary key "); - fail("4"); - } - catch (DriverException exception) {} - - assertEquals(0, driver.executeUpdate("ALTER TABLE person RENAME TO Person2")); // Renames the table. - - try // Tries to rename an unknown table. - { - driver.executeUpdate("ALTER TABLE peRson RENAME to person2 "); - fail("5"); - } - catch (DriverException exception) {} - - try // Tries to add a primary key to an unknown table. - { - driver.executeUpdate("ALTER TABLE peRson add primary key (age) "); - fail("6"); - } - catch (DriverException exception) {} - - try // Tries to drop a primary key from an unknown table. - { - driver.executeUpdate("ALTER TABLE person drop primary key "); - fail("7"); - } - catch (DriverException exception) {} - - assertEquals(-1, selectOldTable()); // The old table does not exist anymore, so the select will fail. - - // Does a select on the new table. - output("Select on new table"); - ResultSet rs = driver.executeQuery("Select name, age from person2 "); - assertEquals(2, rs.getRowCount()); - rs.close(); - - assertEquals(-1, createIndexNewTable()); // The index already exists from the old table. - - // But to drop the index the new table must be used instead. - try - { - driver.executeUpdate("Drop index age on person"); - fail("8"); - } - catch (DriverException exception) {} - assertEquals(1, driver.executeUpdate("Drop index age on person2")); - - assertEquals(1, createIndexNewTable()); // Creates the index again. - assertEquals(0, driver.executeUpdate("alter table person2 drop primary key")); // Drops the primary key. - - try // Tries to rename an unknown column. - { - driver.executeUpdate("ALTER TABLE peRson2 RENAME years TO age "); - fail("9"); - } - catch (DriverException exception) {} - - try // Tries to add a primary key to an unknown column. - { - driver.executeUpdate("ALTER TABLE peRson2 add primary key (years) "); - fail("10"); - } - catch (DriverException exception) {} - - assertEquals(0, driver.executeUpdate("ALTER TABLE peRson2 RENAME age TO years ")); // Renames column. - - try // Tries to rename unknown column. - { - driver.executeUpdate("ALTER TABLE peRson2 RENAME age TO years "); - fail("11"); - } - catch (DriverException exception) {} - - try // Tries to add a primary key to an unknown column. - { - driver.executeUpdate("ALTER TABLE peRson2 add primary key (age) "); - fail("12"); - } - catch (DriverException exception) {} - - try // Tries select an unknown column. - { - driver.executeQuery("select age from person2"); - fail("13"); - } - catch (SQLParseException exception) {} - - try // Tries to use an unknown column in a where clause. - { - driver.executeQuery("select * from person2 where age = 1"); - fail("14"); - } - catch (SQLParseException exception) {} - - try // Tries select an unknown column in an aggregation. - { - driver.executeQuery("select max(age) as maximum from person2"); - fail("15"); - } - catch (SQLParseException exception) {} - - try // Tries select an unknown column in a data function. - { - driver.executeQuery("select abs(age) as maximum from person2"); - fail("16"); - } - catch (SQLParseException exception) {} - - try // Tries select use an unknown column in an order by. - { - driver.executeQuery("select * from person2 order by age"); - fail("17"); - } - catch (SQLParseException exception) {} - - try // Tries select use an unknown column in a group by. - { - driver.executeQuery("select * from person2 group by age"); - fail("18"); - } - catch (SQLParseException exception) {} - - assertEquals(-1, createIndexNewColumnTable()); // Since the new table already has an index, the index can't be created. - assertEquals(1, driver.executeUpdate("Drop index years on person2")); // First, it must be dropped. - assertEquals(1, createIndexNewColumnTable()); // Then, re-created. - assertEquals(1, driver.executeUpdate("insert into persOn2(name,years) values ('indira gomes',14)")); // Inserts a new row. - - // Does a select using the new column. - assertEquals(3, (rs = driver.executeQuery("Select name, years from Person2 ")).getRowCount()); - rs.close(); - - assertEquals(1, driver.prepareStatement("Drop index years on person2").executeUpdate()); // Drops the index again. - - // Renames a table and renames it back without using close all between the operations. - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - if (driver.exists("person2")) - driver.executeUpdate("drop table person2"); - driver.execute("create table person(name char(16) primary key, age int)"); - driver.prepareStatement("alter table person rename to person2").executeUpdate(); - driver.prepareStatement("alter table person2 rename to person").executeUpdate(); - driver.prepareStatement("alter table person rename to person2").executeUpdate(); - driver.prepareStatement("alter table person2 drop primary key").executeUpdate(); - driver.prepareStatement("CREATE index ageindex on person2(age)").executeUpdate(); - - // Renames a table and renames it back using close all between the operations. - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - if (driver.exists("person2")) - driver.executeUpdate("drop table person2"); - driver.execute("create table person(name char(16) primary key, age int)"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.executeUpdate("alter table person rename to person2"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.executeUpdate("alter table person2 rename to person"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.executeUpdate("alter table person rename to person2"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.executeUpdate("alter table person2 drop primary key"); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - driver.execute("CREATE index ageindex on person2(age)"); - - String path = driver.getSourcePath(); - driver.closeAll(); - - try // Checks if the files of the old table does not exist and that the files of the new table exist. - { - assertFalse(new File(path + "Test-person.db", File.DONT_OPEN).exists()); - assertTrue(new File(path + "Test-person2.db", File.DONT_OPEN).exists()); - assertFalse(new File(path + "Test-person.dbo", File.DONT_OPEN).exists()); - assertTrue(new File(path + "Test-person2.dbo", File.DONT_OPEN).exists()); - assertFalse(new File(path + "Test-person$2.idk", File.DONT_OPEN).exists()); - assertTrue(new File(path + "Test-person2$2.idk", File.DONT_OPEN).exists()); - } - catch (IOException exception) - { - fail("19"); - } - - } - - /** - * Does a select using the old table. - * - * @return The number of rows or -1 if an exception occurs. - */ - private int selectOldTable() - { - try - { - ResultSet rs = driver.executeQuery("Select name, age from person "); - int ret = rs.getRowCount(); - rs.close(); - return ret; - } - catch (DriverException exception) - { - return -1; - } - } - - /** - * Creates an index in the new table. - * - * @return 1 If the index was successully created or -1 if an exception was thrown. - */ - private int createIndexNewTable() - { - try - { - output("Creating index on new table"); - driver.execute("CREATE index ageindex on person2(age)"); - return 1; - } - catch (AlreadyCreatedException exception) - { - return -1; - } - } - - /** - * Creates an index in the new column. - * - * @return If the index was successully created or -1 if an exception was thrown. - */ - private int createIndexNewColumnTable() - { - try - { - driver.execute("CREATE index ageindex on person2(years)"); - return 1; - } - catch (AlreadyCreatedException exception) - { - return -1; - } - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestResultSet.java b/LitebaseSDK/src/java/samples/sys/testcases/TestResultSet.java deleted file mode 100644 index ffead7ead4..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestResultSet.java +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import totalcross.unit.TestCase; -import litebase.*; - -/** - * Tests many ResultSet public methods that are not tested in the previous test cases, - */ -public class TestResultSet extends TestCase -{ - /** - * Main test method. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - // Creates and populates the test table. - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - driver.execute("create table person(name char(16) , age int, birth Date, years DateTime)"); - driver.execute("create index idx on person(years)"); - driver.executeUpdate("insert into person values ('Renato Novais',12, '2005/9-12 ', ' 2006/08-21 12:08:01:234 ')"); - driver.executeUpdate("insert into person values ('Indira Gomes',13,'2005/7/8 ', '2006/08-21 0:08')"); - driver.executeUpdate("insert into person values ('Lucas Novais',20,'2005/7/8', ' 2008/06/06 13:45 ')"); - driver.executeUpdate("insert into person values ('Zenes Lima',15, '2005/9/12', '2006/08-21 0:08')"); - - testResultSet(driver.executeQuery("select * from person where age > 0 order by name")); // Select with temporary table. - testResultSet(driver.executeQuery("select * from person where age > 0")); // Select without temporary table. - testResultSet(driver.executeQuery("select * from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, name, age, birth, years from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, name, age, birth, years as anos from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, upper(name) as nameAux, age, birth, years as anos from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, lower(name) as nameAux, age, birth, years as anos from person")); // Select without temporary table. - - // Drops some records and adds them again so that there are enpty spaces in the select base table. - driver.executeUpdate("delete from person where age = 12"); - driver.executeUpdate("insert into person values ('Renato Novais',12, '2005/9-12 ', ' 2006/08-21 12:08:01:234 ')"); - driver.executeUpdate("delete from person where age = 20"); - driver.executeUpdate("insert into person values ('Lucas Novais',20,'2005/7/8', ' 2008/06/06 13:45 ')"); - driver.executeUpdate("delete from person where age = 15"); - driver.executeUpdate("insert into person values ('Zenes Lima',15, '2005/9/12', '2006/08-21 0:08')"); - driver.executeUpdate("insert into person values ('Juliana Imperial',29, '1979/06/26', '2009/04-14 0:08')"); - driver.executeUpdate("delete from person where age = 29"); - - testResultSet(driver.executeQuery("select * from person where age > 0 order by name")); // Select with temporary table. - testResultSet(driver.executeQuery("select * from person where age > 0")); // Select without temporary table. - testResultSet(driver.executeQuery("select * from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, name, age, birth, years from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, name, age, birth, years as anos from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, upper(name) as nameAux, age, birth, years as anos from person")); // Select without temporary table. - testResultSet(driver.executeQuery("select rowid, lower(name) as nameAux, age, birth, years as anos from person")); // Select without temporary table. - - driver.closeAll(); - } - - /** - * Tests the result set public methods. - * - * @param resultSet The result set to be tested. - */ - private void testResultSet(ResultSet resultSet) - { - String[][] matrix; - String nameAux; - - assertNotNull(resultSet); - assertEquals(4, resultSet.getRowCount()); - - try // Invalid result set position. - { - resultSet.getStrings(); - fail("1"); - } - catch (DriverException exception) {} - - while (resultSet.next()) - { - try // Checks that the functions lower and upper worked. - { - nameAux = resultSet.getString("nameAux"); - assertTrue(nameAux.equals(nameAux.toLowerCase()) || nameAux.equals(nameAux.toUpperCase())); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - - // Invalid index. - try - { - resultSet.getInt(0); - fail("2"); - } - catch (IllegalArgumentException exception) {} - try - { - resultSet.getDateTime(0); - fail("3"); - } - catch (IllegalArgumentException exception) {} - catch (DriverException exception) {} - try - { - resultSet.isNull(0); - fail("4"); - } - catch (IllegalArgumentException exception) {} - try - { - resultSet.getInt("boboca"); - fail("5"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - try - { - resultSet.getDateTime("boboca"); - fail("6"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - try - { - resultSet.isNull("boboca"); - fail("7"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - try - { - resultSet.getInt(6); - fail("8"); - } - catch (IllegalArgumentException exception) {} - try - { - resultSet.getDateTime(6); - fail("9"); - } - catch (IllegalArgumentException exception) {} - try - { - resultSet.isNull(6); - fail("10"); - } - catch (IllegalArgumentException exception) {} - - // Invalid type or column. - try - { - resultSet.getChars("rowid"); - fail("11"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - try - { - resultSet.getChars("years"); - fail("12"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - try - { - resultSet.getChars("anos"); - fail("13"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - try - { - resultSet.getInt("name"); - fail("14"); - } - catch (DriverException exception) {} - catch (IllegalArgumentException exception) {} - } - - // Tests first() and next(). - resultSet.first(); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.next(); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.next(); - resultSet.next(); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - resultSet.next(); // next() always returns the last result if it has passed the last position. - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests beforeFirst() and next(). - resultSet.beforeFirst(); - resultSet.next(); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.next(); - resultSet.next(); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - resultSet.next(); - resultSet.next(); // next() always return the last result if it has passed the last position. - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests last() and prev(). - resultSet.last(); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.prev(); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.prev(); - resultSet.prev(); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); // prev() always return the first result if it has passed the first position. - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests afterLast() and prev(). - resultSet.afterLast(); - resultSet.prev(); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.prev(); - resultSet.prev(); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); - resultSet.prev(); // prev() always return the first result if it has passed the first position. - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests absolute. - resultSet.absolute(0); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(1); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(2); - assertEquals(2,(matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(3); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests ascending relative with beforeFirst(). - resultSet.beforeFirst(); - resultSet.relative(0); // relative() always set the position to the first or last record if it is in an invalid position. - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.relative(1); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.relative(2); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.relative(3); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.relative(4); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.beforeFirst(); - resultSet.relative(5); // relative() always set the position to the first or last record if it is in an invalid position. - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests ascending relative with first(). - resultSet.first(); - resultSet.relative(0); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.relative(1); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.relative(2); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.relative(3); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.first(); - resultSet.relative(4); // relative() always set the position to the first or last record if it is in an invalid position. - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests descending relative with afterLast(). - resultSet.afterLast(); - resultSet.relative(0); // relative() always set the position to the first or last record if it is in an invalid position. - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.relative(-1); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.relative(-2); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.relative(-3); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.relative(-4); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.afterLast(); - resultSet.relative(-5); // relative() always set the position to the first or last record if it is in an invalid position. - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // Tests descending relative with last(). - resultSet.last(); - resultSet.relative(0); - assertEquals(1, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.relative(-1); - assertEquals(2, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.relative(-2); - assertEquals(3, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.relative(-3); - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - resultSet.last(); - resultSet.relative(-4); // relative() always set the position to the first or last record if it is in an invalid position. - assertEquals(4, (matrix = resultSet.getStrings()).length); - assertBetween(4, matrix[0].length, 5); - - // getStrings() with parameter beginning in the first record. - resultSet.absolute(0); - assertEquals(0, resultSet.getRow()); - assertEquals(0, (matrix = resultSet.getStrings(0)).length); - resultSet.absolute(0); - assertEquals(0, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(1)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(0); - assertEquals(0, resultSet.getRow()); - assertEquals(2, (matrix = resultSet.getStrings(2)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(0); - assertEquals(0, resultSet.getRow()); - assertEquals(3, (matrix = resultSet.getStrings(3)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(0); - assertEquals(0, resultSet.getRow()); - assertEquals(4, (matrix = resultSet.getStrings(4)).length); - assertBetween(4, matrix[0].length, 5); - - // getStrings() with parameter beginning in the second record. - resultSet.absolute(1); - assertEquals(1, resultSet.getRow()); - assertEquals(0, (matrix = resultSet.getStrings(0)).length); - resultSet.absolute(1); - assertEquals(1, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(1)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(1); - assertEquals(1, resultSet.getRow()); - assertEquals(2, (matrix = resultSet.getStrings(2)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(1); - assertEquals(1, resultSet.getRow()); - assertEquals(3, (matrix = resultSet.getStrings(3)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(1); - assertEquals(1, resultSet.getRow()); - assertEquals(3, (matrix = resultSet.getStrings(4)).length); - assertBetween(4, matrix[0].length, 5); - - // getStrings() with parameter beginning in the third record. - resultSet.absolute(2); - assertEquals(2, resultSet.getRow()); - assertEquals(0, (matrix = resultSet.getStrings(0)).length); - resultSet.absolute(2); - assertEquals(2, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(1)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(2); - assertEquals(2, resultSet.getRow()); - assertEquals(2, (matrix = resultSet.getStrings(2)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(2); - assertEquals(2, resultSet.getRow()); - assertEquals(2, (matrix = resultSet.getStrings(3)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(2); - assertEquals(2, resultSet.getRow()); - assertEquals(2, (matrix = resultSet.getStrings(4)).length); - assertBetween(4, matrix[0].length, 5); - - // getStrings() with parameter beginning in the fourth record. - resultSet.absolute(3); - assertEquals(3, resultSet.getRow()); - assertEquals(0, (matrix = resultSet.getStrings(0)).length); - resultSet.absolute(3); - assertEquals(3, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(1)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(3); - assertEquals(3, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(2)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(3); - assertEquals(3, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(3)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(3); - assertEquals(3, resultSet.getRow()); - assertEquals(1, (matrix = resultSet.getStrings(4)).length); - assertBetween(4, matrix[0].length, 5); - - // getStrings() with parameter beginning in the fourth record. - resultSet.absolute(4); - assertEquals(0, (matrix = resultSet.getStrings(0)).length); - resultSet.absolute(4); - assertEquals(1, (matrix = resultSet.getStrings(1)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(4); - assertEquals(1, (matrix = resultSet.getStrings(2)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(4); - assertEquals(1, (matrix = resultSet.getStrings(3)).length); - assertBetween(4, matrix[0].length, 5); - resultSet.absolute(4); - assertEquals(1, (matrix = resultSet.getStrings(4)).length); - assertBetween(4, matrix[0].length, 5); - - try // It is not possible use getStrings() with negative numbers other than -1. - { - resultSet.getStrings(-2); - fail("15"); - } - catch (IllegalArgumentException exception) {} - - resultSet.close(); // Closes the result set. All result set methods must throw an excetion on an atempt to use it, - try - { - resultSet.absolute(1); - fail("16"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.afterLast(); - fail("17"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.close(); - fail("18"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.first(); - fail("19"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getString(1); - fail("20"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getString("name"); - fail("21"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getResultSetMetaData(); - fail("22"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getRow(); - fail("23"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getRowCount(); - fail("24"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.getStrings(); - fail("25"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.isNull(1); - fail("26"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.isNull("name"); - fail("27"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.last(); - fail("28"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.next(); - fail("29"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.prev(); - fail("30"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.relative(1); - fail("31"); - } - catch (IllegalStateException exception) {} - try - { - resultSet.setDecimalPlaces(1, 1); - fail("32"); - } - catch (IllegalStateException exception) {} - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestRowIdAndPurge.java b/LitebaseSDK/src/java/samples/sys/testcases/TestRowIdAndPurge.java deleted file mode 100644 index 526e517358..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestRowIdAndPurge.java +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests the relation between rowid, delete and purge. - */ -public class TestRowIdAndPurge extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - int i = -1; - String[] insertRows = // Rows to insert. - { - "insert into PERSON3 values ('guilherme', 'hazan', 4400.50, 3400.80, 10, 6)", - "insert into PERSON3 values ('raimundo', 'correa', 3400.50, 3400.50, 11, 26)", - "insert into PERSON3 values ('ricardo', 'zorba', 10400.50, 5000.20, 23, 23)", - "insert into PERSON3 values ('cher', 'cher', 1000.50, 3400.50, 4, 3)", "insert into PERSON3 values ('lero', 'lero', 2001.34, 1000.35, 2, 6)", - "insert into PERSON3 values ('zico', 'mengao', 1000.51, 1000.51, 12, 0)", - "insert into PERSON3 values ('roberto', 'dinamite', 2222.51, 1.21, 10, 10)", - "insert into PERSON3 values ('socrates', 'sampaio', 1111.50, 1111.50, 7, 11)", - "insert into PERSON3 values ('paulo', 'falcao', 2800.04, 1.21, 15, 12)", "insert into PERSON3 values ('leo', 'junior', 2.50, 3400.50, 4, 5)" }; - - int numRows = insertRows.length; - - try - { - driver.executeUpdate("drop table PERSON3"); - } - catch (DriverException de) // Table not found. - { - output(de.getMessage()); - } - - // Creates table and index. - driver.execute("create table PERSON3 (FIRST_NAME CHAR(30) NOCASE, LAST_NAME CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, " - + "YEARS_EXP_JAVA INT, YEARS_EXP_C INT )"); - driver.execute("create index idx_name on person3(First_Name)"); - - // Inserts rows. - while (++i < numRows) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - assertEquals(11, driver.getCurrentRowId("person3")); - - // Verifies if the table is ok. - ResultSet rs = driver.executeQuery("select rowid, first_name from person3 where first_name > 'A'"); - i = 0; - while (++i <= numRows) - { - assertTrue(rs.next()); - assertEquals(i, rs.getInt("rowid")); - assertEquals(i + "", rs.getString("rowid")); - } - assertEquals(numRows, rs.getRowCount()); - rs.close(); - - // Tests getRecordCount(). - assertEquals(numRows, driver.getRowCountDeleted("PERSON3") + driver.getRowCount("person3")); - assertEquals(numRows, driver.getRowCount("person3")); - - // Does some selects using like and "%". - assertEquals(1, (rs = driver.executeQuery("select * from person3 where first_name like 'leo%'")).getRowCount()); - rs.close(); - assertEquals(1, (rs = driver.executeQuery("select * from person3 where last_name like '%orr%'")).getRowCount()); - rs.close(); - - // Deletes some records. - assertEquals(2, driver.executeUpdate("delete person3 where first_name like 'l%'")); - - // Tests getRecordCount() again. - assertEquals(numRows, driver.getRowCountDeleted("person3") + driver.getRowCount("person3")); - assertEquals(numRows - 2, driver.getRowCount("person3")); - - // Purges the table. - assertEquals(2, driver.purge("PERSON3")); - - // Tests getRecordCount(). - assertEquals(numRows -= 2, driver.getRowCountDeleted("person3") + driver.getRowCount("person3")); - assertEquals(numRows, driver.getRowCount("person3")); - - // Verifies if the table is ok. - rs = driver.executeQuery("select rowid, first_name from person3 where first_name > 'A'"); - i = 0; - while (++i <= numRows) - { - if (i == 5) - continue; - assertTrue(rs.next()); - assertEquals(i, rs.getInt("rowid")); - } - assertEquals(numRows, rs.getRowCount()); - rs.close(); - driver.closeAll(); - - driver = AllTests.getInstance("Test"); - assertEquals(11, driver.getCurrentRowId("person3")); - - // Inserts rows. - i = -1; - while (++i < numRows) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - numRows <<= 1; - - assertEquals(19, driver.getCurrentRowId("person3")); // 2 * 10 - 2 + 1 - - // Verifies if the table is ok. - rs = driver.executeQuery("select rowid, first_name from person3 where first_name > 'A'"); - i = 0; - while (++i <= numRows) - { - if (i == 5 || i == 10) - continue; - assertTrue(rs.next()); - assertEquals(i, rs.getInt("rowid")); - } - rs.close(); - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestRowIterator.java b/LitebaseSDK/src/java/samples/sys/testcases/TestRowIterator.java deleted file mode 100644 index c9addb7158..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestRowIterator.java +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.Time; -import totalcross.unit.*; -import totalcross.util.*; - -/** - * Tests the RowIterator class. - */ -public class TestRowIterator extends TestCase -{ - /** - * The mais test method. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - int i = -1, - updated = 0, - deleted = 0, - newed = 0, - synced = 0; - - try // Drops the table. - { - driver.executeUpdate("drop table person"); - } - catch (DriverException exception) {} - - try // Creates the table. - { - driver.execute("CREATE TABLE PERSON (NAME CHAR(500) NOCASE, ADDRESS char(700), id int, age short, cpf long, salary float, " - + "size double, birth date, arrival datetime, pic blob(20))"); - } - catch (AlreadyCreatedException exception) - { - fail("1"); - } - - PreparedStatement ps = driver.prepareStatement("INSERT INTO person VALUES (?, ?, ?, ?, ?, ?, ?, '2005/9-12', '2006/08-21 12:08:01:234', ?)"); - - while (++i < 20) // Populates the table. - { - ps.setString(0, "Name" + i); - ps.setString(1, "Addr" + i); - ps.setInt(2, i); - ps.setShort(3, (short)(i + 10)); - ps.setLong(4, 111111111 + i); - ps.setFloat(5, 1000.00 + i); - ps.setDouble(6, 3.1415 + i); - ps.setBlob(7, ("Name" + i).getBytes()); - assertEquals(1, ps.executeUpdate()); - } - - RowIterator it = driver.getRowIterator("PERSON"); // Gets the row iterator. - - i = 0; - while (it.next()) - { - // Confirms the row id and that the NEW attribute is set. - assertEquals(++i, it.rowid); - assertEquals(RowIterator.ROW_ATTR_NEW, it.attr); - - if (5 <= i && i <= 15) // Simulates that these rows have been synchronized. - it.setSynced(); - } - - // Tests the nextNotSynced(). - it.reset(); - i = 0; - while (it.nextNotSynced()) - i++; - assertEquals(9, i); - - // Changes some rows. - driver.executeUpdate("update person set name ='guilherme' where name like '%1'"); - driver.executeUpdate("delete person where rowid = 2"); - - it.reset(); - i = 0; - - while (it.next()) // Iterates through the iterator. - { - // Checks the values of the iterator. - if (i % 10 != 1) - assertEquals("Name" + i, it.getString(1)); - else - assertEquals("guilherme", it.getString(1)); - assertFalse(it.isNull(1)); - assertEquals("Addr" + i, it.getString(2)); - assertEquals(i, it.getInt(3)); - assertEquals(10 + i, it.getShort(4)); - assertEquals(111111111 + i, it.getLong(5)); - assertEquals(1000.00 + i, it.getFloat(6), 0.0000001); - assertEquals(3.1415 + i, it.getDouble(7), 0.0000001); - try - { - assertEquals(new Date(20050912), it.getDate(8)); - } - catch (InvalidDateException exception) {} - assertEquals(new Time(2006,8,21,12,8,01,234), it.getDateTime(9)); - assertEquals(("Name" + i++).getBytes(), it.getBlob(10)); - - // Index out of bounds. - try - { - it.getString(-1); - fail("2"); - } - catch (IllegalArgumentException exception) {} - try - { - it.getString(11); - fail("3"); - } - catch (IllegalArgumentException exception) {} - - try // Wrong type - { - it.getBlob(1); - fail("4"); - } - catch (DriverException exception) {} - - switch (it.attr) // Tests the attributtes of the row. - { - case RowIterator.ROW_ATTR_DELETED: - it.setSynced(); - deleted++; - break; - case RowIterator.ROW_ATTR_SYNCED: - synced++; - break; - case RowIterator.ROW_ATTR_NEW: - newed++; - break; - case RowIterator.ROW_ATTR_UPDATED: - updated++; - } - } - - assertEquals(updated, 1); - assertEquals(deleted, 1); - assertEquals(newed, 8); - assertEquals(synced, 10); - it.close(); - - // The row iterator can't be used if it is closed. - // close() and reset() need not be checked. - try - { - it.next(); - fail("5"); - } - catch (IllegalStateException exception) {} - try - { - it.getString(1); - fail("6"); - } - catch (IllegalStateException exception) {} - try - { - it.nextNotSynced(); - fail("7"); - } - catch (IllegalStateException exception) {} - try - { - it.setSynced(); - fail("8"); - } - catch (IllegalStateException exception) {} - try - { - it.close(); - fail("9"); - } - catch (IllegalStateException exception) {} - - driver.closeAll(); - deleted = synced = newed = updated = 0; - driver = AllTests.getInstance("Test"); - - // Inserts a new record. - driver.executeUpdate("INSERT INTO PERSON (NAME) VALUES ('Juliana')"); - - // Checks the attributes again. - it = driver.getRowIterator("PERSON"); // Gets the iterator for the table. - while (it.next()) // Iterates through the iterator. - switch (it.attr) // Tests the attributtes of the row. - { - case RowIterator.ROW_ATTR_DELETED: - it.setSynced(); - deleted++; - break; - case RowIterator.ROW_ATTR_SYNCED: - synced++; - break; - case RowIterator.ROW_ATTR_NEW: - newed++; - break; - case RowIterator.ROW_ATTR_UPDATED: - updated++; - } - assertEquals(updated, 1); - assertEquals(deleted, 1); // The deleted row is still here. - assertEquals(newed, 9); // One more new row. - assertEquals(synced, 10); - - deleted = synced = newed = updated = 0; - it.close(); - driver.purge("Person"); // Eliminates the deleted row. - it = driver.getRowIterator("person"); // It is necessary to get a new one. - - // Checks agaim. - while (it.next()) // Iterates through the iterator. - switch (it.attr) // Tests the attributtes of the row. - { - case RowIterator.ROW_ATTR_DELETED: - it.setSynced(); - deleted++; - break; - case RowIterator.ROW_ATTR_SYNCED: - synced++; - break; - case RowIterator.ROW_ATTR_NEW: - newed++; - break; - case RowIterator.ROW_ATTR_UPDATED: - updated++; - } - assertEquals(updated, 1); - assertEquals(deleted, 0); // The deleted row is not here anymore. - assertEquals(newed, 9); - assertEquals(synced, 10); - - // Tests row iterator with null values. - assertEquals(20, driver.executeUpdate("update person set name = null")); - it = driver.getRowIterator("person"); // It is necessary to get a new one. - while (it.next()) // Iterates through the iterator. - { - assertNull(it.getString(1)); - assertTrue(it.isNull(1)); - assertTrue(it.attr == RowIterator.ROW_ATTR_UPDATED || it.attr == RowIterator.ROW_ATTR_NEW); - } - - it.reset(); - - assertEquals(1, driver.executeUpdate("delete person where rowid = 1")); - - // All non-deleted rows are marked as new. - while (it.next()) - it.setNotSynced(); - it.reset(); - while (it.next()) - if (it.rowid == 1) - assertEquals(it.attr, RowIterator.ROW_ATTR_DELETED); - else - assertEquals(it.attr, RowIterator.ROW_ATTR_NEW); - - driver.closeAll(); - it.reset(); - - // The row iterator must not be used with the driver closed. - try - { - it.next(); - fail("10"); - } - catch (IllegalStateException exception) {} - try - { - it.nextNotSynced(); - fail("11"); - } - catch (IllegalStateException exception) {} - try - { - it.setSynced(); - fail("12"); - } - catch (IllegalStateException exception) {} - try - { - it.getShort(4); - fail("13"); - } - catch (IllegalStateException exception) {} - try - { - it.getInt(3); - fail("14"); - } - catch (IllegalStateException exception) {} - try - { - it.getLong(5); - fail("15"); - } - catch (IllegalStateException exception) {} - try - { - it.getFloat(6); - fail("16"); - } - catch (IllegalStateException exception) {} - try - { - it.getDouble(7); - fail("17"); - } - catch (IllegalStateException exception) {} - try - { - it.getString(1); - fail("18"); - } - catch (IllegalStateException exception) {} - try - { - it.getBlob(10); - fail("19"); - } - catch (IllegalStateException exception) {} - try - { - it.getDate(8); - fail("20"); - } - catch (IllegalStateException exception) {} - try - { - it.getDateTime(9); - fail("21"); - } - catch (IllegalStateException exception) {} - try - { - it.isNull(1); - fail("22"); - } - catch (IllegalStateException exception) {} - - try - { - it.close(); - fail("23"); - } - catch (IllegalStateException exception) {} - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestSQLFunctions.java b/LitebaseSDK/src/java/samples/sys/testcases/TestSQLFunctions.java deleted file mode 100644 index 0d905d6b78..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestSQLFunctions.java +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * This test verifies if all Litebase data type functions are working properly - * FUNCTIONS: year, month, day, hour, minute, second, millis, upper, lower, and abs // rnovais@570_2. - * - * ABS(short, long, float, double) // rnovais@570_6. - * - * month(birth) = 2 and year(birth) = 2006 // rnovais@570_108. - */ -public class TestSQLFunctions extends TestCase -{ - LitebaseConnection driver = AllTests.getInstance("Test"); - - /** - * The main test method. - */ - public void testRun() - { - ResultSet resultSet; - - // First inserts the items into a new table. - output("Create simple table and insert."); - try - { - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - driver.execute("create table person(name char(16), amount int, amount1 short, amount2 long, amount3 float, amount4 double, birth Date, " - + "years DateTime)"); - driver.executeUpdate("insert into person values ('Renato Novais',-1,-2,-100l,-1.2,-456.0, ' 2007/5-3 ', ' 2007/11-2 12:08:01:234 ')"); - driver.executeUpdate("insert into person values ('indira gomes',13,-8,-25l,5.2,-154.0, '2006/7/8 ', '2006/08-21 0:08')"); - driver.executeUpdate("insert into person values ('Lucas Novais',-20,-456,48L,-5.9,-954.2, '2008/4/6', ' 2008/06/06 13:45 ')"); - driver.executeUpdate("insert into person values ('Zenes Lima',-15,-54, -5698L,-8.3,-456.5, '2005/9/12 ', '2005/01-4 1:50')"); - } - catch (DriverException exception) - { - fail("1"); - } - - // Tests functions that are properly applied. - // month(DATE). - assertNotNull(resultSet = driver.executeQuery("Select month(years) as mon1, years from person")); - assertEquals(4, resultSet.getRowCount()); - assertTrue(resultSet.next()); - try - { - resultSet.getInt(1); - fail("2"); - } - catch (DriverException exception) {} - assertEquals(11, resultSet.getShort(1)); - assertTrue(resultSet.next()); - assertEquals(resultSet.getShort(1), 8); - assertTrue(resultSet.next()); - assertEquals(resultSet.getShort("mon1"), 6); - assertTrue(resultSet.next()); - assertEquals(1, resultSet.getShort(1)); - assertFalse(resultSet.next()); - - // Tests functions with getStrings(). - assertTrue(resultSet.first()); - String[][] strings = resultSet.getStrings(); - assertEquals(4, strings.length); - assertEquals("11", strings[0][0]); - assertEquals("8", strings[1][0]); - assertEquals("6", strings[2][0]); - assertEquals("1", strings[3][0]); - resultSet.close(); - - // years(DATE). - assertNotNull(resultSet = driver.executeQuery("Select year(years) as mon1, years from person where day(years) >= 6")); - assertEquals(2, resultSet.getRowCount()); - assertTrue(resultSet.next()); - try - { - resultSet.getLong(1); - fail("3"); - } - catch (DriverException exception) {} - assertEquals(2006, resultSet.getShort(1)); - assertTrue(resultSet.next()); - assertEquals(2008, resultSet.getShort("mon1")); - assertFalse(resultSet.next()); - resultSet.close(); - - assertNotNull(resultSet = driver.executeQuery("Select hour(years) as h1, day(birth) as d1 from person where month(birth) != 7 " + - " and hour(years) != 0")); - assertEquals(3, resultSet.getRowCount()); - assertTrue(resultSet.next()); - try - { - resultSet.getDate(1); - fail("4"); - } - catch (DriverException exception) {} - try - { - resultSet.getDateTime(2); - fail("5"); - } - catch (DriverException exception) {} - assertEquals(12, resultSet.getShort(1)); - assertEquals(3, resultSet.getShort(2)); - assertEquals("12\t3", resultSet.rowToString()); - assertTrue(resultSet.next()); - assertEquals(13, resultSet.getShort("h1")); - assertEquals(6, resultSet.getShort("d1")); - assertEquals("13\t6", resultSet.rowToString()); - assertTrue(resultSet.next()); - assertEquals(1, resultSet.getShort(1)); - assertEquals(12, resultSet.getShort(2)); - assertEquals("1\t12", resultSet.rowToString()); - assertFalse(resultSet.next()); - resultSet.close(); - - assertNotNull(resultSet = driver.executeQuery("Select millis(years) as mil1, minute(years) as sec1 from person where birth > '2005/9-12' " - + "order by years")); - assertEquals(3, resultSet.getRowCount()); - assertTrue(resultSet.next()); - try - { - resultSet.getFloat(1); - fail("6"); - } - catch (DriverException exception) {} - try - { - resultSet.getDouble(2); - fail("7"); - } - catch (DriverException exception) {} - assertEquals(0, resultSet.getShort("mil1")); - assertEquals(8, resultSet.getShort("sec1")); - assertTrue(resultSet.next()); - assertEquals(234, resultSet.getShort(1)); - assertEquals(8, resultSet.getShort(2)); - assertTrue(resultSet.next()); - assertEquals(0, resultSet.getShort(1)); - assertEquals(45, resultSet.getShort(2)); - assertFalse(resultSet.next()); - resultSet.close(); - - assertNotNull(resultSet = driver.executeQuery("Select year(birth) as y1, month(birth) as m1, day(birth) as d1 from person " + - " where year(birth) = 2005")); - assertEquals(1, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(2005, resultSet.getShort(1)); - assertEquals(9, resultSet.getShort(2)); - assertEquals(12, resultSet.getShort(3)); - assertFalse(resultSet.next()); - resultSet.close(); - - assertNotNull(resultSet = driver.executeQuery("Select hour(years) as h1, minute(years) as m1, second(years) as d1 from person " + - "where hour(years) >= 12")); - assertEquals(2, resultSet.getRowCount()); - assertTrue(resultSet.next()); - try - { - resultSet.getFloat(1); - fail("8"); - } - catch (DriverException exception) {} - assertEquals(12, resultSet.getShort(1)); - assertEquals(8, resultSet.getShort(2)); - assertEquals(1, resultSet.getShort(3)); - assertTrue(resultSet.next()); - assertEquals(13, resultSet.getShort("h1")); - assertEquals(45, resultSet.getShort("m1")); - assertEquals(0, resultSet.getShort("d1")); - assertFalse(resultSet.next()); - resultSet.close(); - - // rnovais@570_2: ABS. - assertNotNull(resultSet = driver.executeQuery("Select amount, abs(amount) as a1 from person where abs(amount)>13")); - assertEquals(2, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(-20, resultSet.getInt(1)); - assertEquals(20, resultSet.getInt(2)); - assertTrue(resultSet.next()); - assertEquals(-15, resultSet.getInt(1)); - assertEquals(15, resultSet.getInt("a1")); - assertFalse(resultSet.next()); - resultSet.close(); - - // rnovais@570_2: UPPER, LOWER. - assertNotNull(resultSet = driver.executeQuery("Select amount, abs(amount) as a1, name, lower(name) as u1, upper(name) as u2 from person where" - + " abs(amount)>12 and UPPER(name) > 'INDIRA GOMES'")); - assertEquals(2, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(-20, resultSet.getInt(1)); - assertEquals(20, resultSet.getInt(2)); - assertEquals("Lucas Novais", resultSet.getString(3)); - assertEquals("lucas novais", resultSet.getString(4)); - assertEquals("LUCAS NOVAIS",resultSet.getString(5)); - assertEquals("-20\t20\tLucas Novais\tlucas novais\tLUCAS NOVAIS", resultSet.rowToString()); - assertTrue(resultSet.next()); - assertEquals(-15, resultSet.getInt(1)); - assertEquals(15, resultSet.getInt("a1")); - assertEquals("Zenes Lima", resultSet.getString(3)); - assertEquals("zenes lima", resultSet.getString("u1")); - assertEquals("ZENES LIMA", resultSet.getString("u2")); - assertEquals("-15\t15\tZenes Lima\tzenes lima\tZENES LIMA", resultSet.rowToString()); - assertFalse(resultSet.next()); - - // Tests functions with getStrings(). - assertTrue(resultSet.first()); - assertEquals(2, (strings = resultSet.getStrings()).length); - assertEquals("Lucas Novais", strings[0][2]); - assertEquals("lucas novais", strings[0][3]); - assertEquals("LUCAS NOVAIS", strings[0][4]); - assertEquals("Zenes Lima", strings[1][2]); - assertEquals("zenes lima", strings[1][3]); - assertEquals("ZENES LIMA", strings[1][4]); - resultSet.close(); - - // rnovais@570_6 - assertNotNull(resultSet = driver.executeQuery("Select abs(amount) as a0, abs(amount1) as a1, abs(amount2) as a2, abs(amount3) as a3, " - + "abs(amount4) as a4 from person where abs(amount)>13")); - assertEquals(2, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(20, resultSet.getInt(1)); - assertEquals(456, resultSet.getShort(2)); - assertEquals(48, resultSet.getLong(3)); - assertEquals(5.9, resultSet.getFloat(4), 1e-3); - assertEquals(954.2, resultSet.getDouble(5), 1e-3); - assertTrue(resultSet.next()); - assertEquals(15, resultSet.getInt("a0")); - assertEquals(54, resultSet.getShort("a1")); - assertEquals(5698, resultSet.getLong("a2")); - assertEquals(8.30, resultSet.getFloat("a3"), 1e-3); - assertEquals(456.5, resultSet.getDouble("a4"), 1e-3); - assertFalse(resultSet.next()); - resultSet.close(); - - // rnovais@570_108 - assertNotNull(resultSet = driver.executeQuery("Select amount from person where month(birth) = 7 and year(birth) = 2006 and day(birth) != 9 and" - + " hour(years) = 0")); - assertEquals(1, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(13, resultSet.getInt(1)); - assertFalse(resultSet.next()); - resultSet.close(); - - assertNotNull(resultSet = driver.executeQuery("Select amount from person where month(birth) < 5 and birth = '2008/4/6'")); - assertEquals(1, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(-20, resultSet.getInt(1)); - assertFalse(resultSet.next()); - resultSet.close(); - - assertNotNull(resultSet = driver.executeQuery("Select upper(name) as n1 from person where month(birth) >= 5 and birth != '2005/9/12'")); - assertEquals(2, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals("RENATO NOVAIS", resultSet.getString(1)); - assertTrue(resultSet.next()); - assertEquals("INDIRA GOMES", resultSet.getString("n1")); - assertFalse(resultSet.next()); - resultSet.close(); - - // rnovais@112_1 - assertNotNull(resultSet = driver.executeQuery("Select amount from person where lower(name) >= 'zenes lima'")); - assertEquals(1, resultSet.getRowCount()); - assertTrue(resultSet.next()); - assertEquals(-15, resultSet.getInt(1)); - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests functions that aren't compatible with a data type. - output("function that isn't compatible with data type: millis x date"); - try - { - driver.executeQuery("Select millis(birth) as mil, years from person"); - fail("9"); - } - catch (SQLParseException exception) {} - output("function that isn't compatible with data type: second x date"); - try - { - driver.executeQuery("Select year(birth) as y1, month(birth) as m1, day(birth) as d1 from person where second(birth) = 234"); - fail("10"); - } - catch (SQLParseException exception) {} - output("function that isn't compatible with data type: minute x date"); - try - { - driver.executeQuery("Select age from person where hour(birth) = 12"); - fail("11"); - } - catch (SQLParseException exception) {} - output("function that isn't compatible with data type: hour x date"); - try - { - driver.executeQuery("Select age from person where hour(birth) = 12"); - fail("12"); - } - catch (SQLParseException exception) {} - output("function that isn't compatible with data type: upper x date"); // rnovais@570_2 - try - { - driver.executeQuery("Select upper(birth) as y1 from person"); - fail("13"); - } - catch (SQLParseException exception) {} - output("function that isn't compatible with data type: abs x char"); // rnovais@570_2 - try - { - driver.executeQuery("Select abs(name) as y1 from person"); - fail("14"); - } - catch (SQLParseException exception) {} - - try // There's no alias. - { - driver.executeQuery("Select month(birth) from person "); - fail("15"); - } - catch (SQLParseException exception) {} - - driver.closeAll(); - } -} \ No newline at end of file diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestSelectClause_AggFunctions.java b/LitebaseSDK/src/java/samples/sys/testcases/TestSelectClause_AggFunctions.java deleted file mode 100644 index 5faf25dfa4..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestSelectClause_AggFunctions.java +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests aggregation functions. - */ -public class TestSelectClause_AggFunctions extends TestCase -{ - /** - * The mais test method. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - try - { - driver.executeUpdate("drop table PERSON"); - } - catch (DriverException exception) {} // Table not found. - - // Rows to insert - String [] insertRows = - { - "insert into PERSON values ('getulio', 'Sorocaba', 'sp', 2778.11, 42)", - "insert into PERSON values ('guilherme', 'Rio de Janeiro', 'rj', 4400.50, 35)", - "insert into PERSON values ('roberto', 'Rio de Janeiro', 'rj', 3900.50, 72)", - "insert into PERSON values ('joao', 'Petropolis', 'rj', 5004.26, 44)", - "insert into PERSON values ('ricardo', 'Sao Paulo', 'sp', 2876.22, 23)", - "insert into PERSON values ('janos', 'Ribeirao Preto', 'sp', 10082.12, 65)", - "insert into PERSON values ('socrates', 'Sao Paulo', 'sp', 5100.12, 48)", - "insert into PERSON values ('dudamel', 'Ribeirao Preto', 'sp', 3999.34, 34)", - "insert into PERSON values ('felipe', 'Ribeirao Preto', 'sp', 2778.11, 42)", - "insert into PERSON values ('carmelo', 'Porto Alegre', 'rs', 6789.66, 36)", - "insert into PERSON values ('breno', 'Uruguaiana', 'rs', 4441.88, 26)", - "insert into PERSON values ('mauricio', 'Rio de Janeiro', 'rj', 7777.88, 38)", - "insert into PERSON values ('maria', 'Sorocaba', 'sp', 3456.22, 28)", - "insert into PERSON values ('marta', 'Petropolis', 'rj', 8788.22, 19)", - "insert into PERSON values ('renato', 'Rio de Janeiro', 'rj', 5555.33, 44)", - "insert into PERSON values ('nadia', 'Porto Alegre', 'rs', 4006.32, 62)" - }; - - int numRows = insertRows.length; - - // Creates the table. - driver.execute("create table PERSON (FIRST_NAME CHAR(30), CITY CHAR(30), STATE CHAR(2), SALARY FLOAT, AGE SHORT)"); - - // Inserts rows. - int i = -1; - while (++i < numRows) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - // Runs SQL queries to test the where clause parsing and evaluation. - - // No where clause and select with wildcard. - assertEquals(numRows, executeQuery(driver, "select * from PERSON")); - - // Simple where clause. - assertEquals(7, executeQuery(driver, "select first_name, age, CITY from PERSON where state ='sp'")); - - // Simple where clause with aliases. - assertEquals(7, executeQuery(driver, "select first_name as nome, age as idade, CITY from PERSON where state ='sp'")); - - // Simple COUNT(*) - ResultSet resultSet = driver.executeQuery("select COUNT(*) as total_records from PERSON"); - assertTrue(resultSet.next()); - - assertEquals(numRows, resultSet.getInt("total_records")); // Asserts that query worked properly. - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests MAX() and MIN() for string types. - assertTrue((resultSet = driver.executeQuery("select max(FIRST_NAME) as max_name, min(FIRST_NAME) as min_name, MAX(city) as max_city, " - + "MIN(city) as min_city from PERSON")).next()); - assertEquals("socrates", resultSet.getString("max_name")); - assertEquals("breno", resultSet.getString("min_name")); - assertEquals("Petropolis", resultSet.getString("min_city")); - assertEquals("Uruguaiana", resultSet.getString("max_city")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests MAX() and MIN() for string types with having. - assertTrue((resultSet = driver.executeQuery("select max(FIRST_NAME) as max_name, count(*) as total, state from PERSON group by state " + - "having max_name >= 'nadia'")).next()); - assertEquals(3, resultSet.getRowCount()); - assertEquals("roberto", resultSet.getString(1)); - assertEquals(6, resultSet.getInt(2)); - assertEquals("rj", resultSet.getString(3)); - assertTrue(resultSet.next()); - assertEquals("nadia", resultSet.getString(1)); - assertEquals(3, resultSet.getInt(2)); - assertEquals("rs", resultSet.getString(3)); - assertTrue(resultSet.next()); - assertEquals("socrates", resultSet.getString(1)); - assertEquals(7, resultSet.getInt(2)); - assertEquals("sp", resultSet.getString(3)); - assertFalse(resultSet.next()); - resultSet.close(); - - assertTrue((resultSet = driver.executeQuery("select min(FIRST_NAME) as min_name, count(*) as total, state from PERSON group by state " + - "having min_name <= 'guilherme'")).next()); - assertEquals(3, resultSet.getRowCount()); - assertEquals("guilherme", resultSet.getString(1)); - assertEquals(6, resultSet.getInt(2)); - assertEquals("rj", resultSet.getString(3)); - assertTrue(resultSet.next()); - assertEquals("breno", resultSet.getString(1)); - assertEquals(3, resultSet.getInt(2)); - assertEquals("rs", resultSet.getString(3)); - assertTrue(resultSet.next()); - assertEquals("dudamel", resultSet.getString(1)); - assertEquals(7, resultSet.getInt(2)); - assertEquals("sp", resultSet.getString(3)); - assertFalse(resultSet.next()); - resultSet.close(); - - // Simple COUNT(*) with where clause. - assertTrue((resultSet = driver.executeQuery("select COUNT(*) as total_records from PERSON where state = 'sp'")).next()); - assertEquals(7, resultSet.getInt("total_records")); // Asserts that query worked properly. - assertFalse(resultSet.next()); - resultSet.close(); - - // Aggregated functions without where clause or group by function. - assertTrue((resultSet = driver.executeQuery("select COUNT(*) as total, min(age) as min_age, max(age) as max_age, sum(salary) as sum_salary, " - + "avg(salary) as avg_salary from PERSON")).next()); - - // Asserts that query worked properly. - assertEquals(16, resultSet.getInt("total")); - assertEquals(19, resultSet.getShort("min_age")); - assertEquals(72, resultSet.getShort("max_age")); - assertEquals(81734.79, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(5108.42, resultSet.getDouble("avg_salary"), 1e-2); - assertFalse(resultSet.next()); - resultSet.close(); - - // Where clause with aggregated functions and no group by clause. - assertTrue((resultSet = driver.executeQuery("select COUNT(*) as total, min(age) as min_age, max(age) as max_age, sum(salary) as sum_salary, " - + "avg(salary) as avg_salary from PERSON where state ='sp'")).next()); - - // Asserts that query worked. - assertEquals(7, resultSet.getInt("total")); - assertEquals(23, resultSet.getShort("min_age")); - assertEquals(65, resultSet.getShort("max_age")); - assertEquals(31070.25, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(4438.60, resultSet.getDouble("avg_salary"), 1e-2); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with aggregated functions and simple columns with group by clause. - assertTrue((resultSet = driver.executeQuery("select CITY, state, COUNT(*) as total, min(age) as min_age, avg(age) as avg_age, "+ - "sum(salary) as sum_salary, avg(salary) as avg_salary from PERSON group by state,CITY")).next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(7, resultSet.getRowCount()); - - // First record: Petropolis RJ 2 19 31.5 13792.48 6896.2399999999998. - assertEquals(2, resultSet.getInt("total")); - assertEquals(19, resultSet.getShort("min_age")); - assertEquals("rj", resultSet.getString("state")); - assertEquals("Petropolis", resultSet.getString("CITY")); - assertEquals(31.501, resultSet.getDouble("avg_age"), 1e-2); - assertEquals(13792.48, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(6896.24, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Second record: Rio de Janeiro RJ 4 35 47.25 21634.209999999999 5408.5524999999998. - assertEquals(4, resultSet.getInt("total")); - assertEquals(35, resultSet.getShort("min_age")); - assertEquals("rj", resultSet.getString("state")); - assertEquals("Rio de Janeiro", resultSet.getString("CITY")); - assertEquals(47.251, resultSet.getDouble("avg_age"), 1e-2); - assertEquals(21634.21, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(5408.55, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Third record: Porto Alegre RS 2 36 49.0 10795.98 5397.9899999999998. - assertEquals(2, resultSet.getInt("total")); - assertEquals(36, resultSet.getShort("min_age")); - assertEquals("rs", resultSet.getString("state")); - assertEquals("Porto Alegre", resultSet.getString("CITY")); - assertEquals(49.01, resultSet.getDouble("avg_age"), 1e-2); - assertEquals(10795.98, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(5397.99, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Fourth record: Uruguaiana RS 1 26 26.0 4441.8800000000001 4441.8800000000001. - assertEquals(1, resultSet.getInt("total")); - assertEquals(26, resultSet.getShort("min_age")); - assertEquals("rs", resultSet.getString("state")); - assertEquals("Uruguaiana", resultSet.getString("CITY")); - assertEquals(26.0, resultSet.getDouble("avg_age"), 1e-1); - assertEquals(4441.88, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(4441.88, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Fifth record: Ribeirao Preto SP 3 34 47.0 16859.57 5619.8566666666666. - assertEquals(3, resultSet.getInt("total")); - assertEquals(34, resultSet.getShort("min_age")); - assertEquals("sp", resultSet.getString("state")); - assertEquals("Ribeirao Preto", resultSet.getString("CITY")); - assertEquals(47.0, resultSet.getDouble("avg_age"), 1e-1); - assertEquals(16859.57, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(5619.86, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Sixth record: Sao Paulo SP 2 23 35.5 7976.3400000000001 3988.1700000000001. - assertEquals(2, resultSet.getInt("total")); - assertEquals(23, resultSet.getShort("min_age")); - assertEquals("sp", resultSet.getString("state")); - assertEquals("Sao Paulo", resultSet.getString("CITY")); - assertEquals(35.5, resultSet.getDouble("avg_age"), 1e-1); - assertEquals(7976.35, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(3988.18, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Seventh record: Sorocaba SP 2 28 35.0 6234.3299999999999 3117.165. - assertEquals(2, resultSet.getInt("total")); - assertEquals(28, resultSet.getShort("min_age")); - assertEquals("sp", resultSet.getString("state")); - assertEquals("Sorocaba", resultSet.getString("CITY")); - assertEquals(35.0, resultSet.getDouble("avg_age"), 1e-1); - assertEquals(6234.33,resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(3117.16, resultSet.getDouble("avg_salary"), 1e-2); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with order by. - assertTrue((resultSet = driver.executeQuery("select first_name as name, CITY from PERSON order by age, first_name desc")).next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(16, resultSet.getRowCount()); - assertEquals("marta", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("ricardo", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("breno", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("maria", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("dudamel", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("guilherme", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("carmelo", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("mauricio", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("getulio", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("felipe", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("renato", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("joao", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("socrates", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("nadia", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("janos", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("roberto", resultSet.getString("name")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with order by and where clause - assertTrue((resultSet = driver.executeQuery("select first_name as name, CITY from PERSON where state = 'sp' or state = 'rs' " - + "order by CITY, first_name desc")).next()); - - // Asserts that the query worked, by checking the values of each record. - assertEquals(10, resultSet.getRowCount()); - - assertEquals("nadia", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("carmelo", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("janos", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("felipe", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("dudamel", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("socrates", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("ricardo", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("maria", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("getulio", resultSet.getString("name")); - assertTrue(resultSet.next()); - assertEquals("breno", resultSet.getString("name")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with where clause and group by. - assertTrue((resultSet = driver.executeQuery("select COUNT(*) as total, CITY from PERSON where age > 26 and salary > 3000F group by CITY")) - .next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(6, resultSet.getRowCount()); - assertEquals("Petropolis", resultSet.getString("CITY")); - assertEquals(1, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Porto Alegre", resultSet.getString("CITY")); - assertEquals(2, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Ribeirao Preto", resultSet.getString("CITY")); - assertEquals(2, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Rio de Janeiro",resultSet.getString("CITY")); - assertEquals(4, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Sao Paulo", resultSet.getString("CITY")); - assertEquals(1, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Sorocaba", resultSet.getString("CITY")); - assertEquals(1, resultSet.getInt("total")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with order by, group by, and where clause. - assertTrue((resultSet = driver.executeQuery("select COUNT(*) as total, CITY from PERSON where state = 'sp' or state = 'rj' " + - "group by CITY order by CITY")).next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(5, resultSet.getRowCount()); - assertEquals("Petropolis", resultSet.getString("CITY")); - assertEquals(2, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Ribeirao Preto", resultSet.getString("CITY")); - assertEquals(3, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Rio de Janeiro", resultSet.getString("CITY")); - assertEquals(4, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Sao Paulo", resultSet.getString("CITY")); - assertEquals(2, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Sorocaba", resultSet.getString("CITY")); - assertEquals(2, resultSet.getInt("total")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with group by but no aggregated function to simulate the usage of a "distinct" in the select. - assertTrue((resultSet = driver.executeQuery("select CITY from PERSON where state = 'sp' or state = 'rj' group by CITY")).next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(5, resultSet.getRowCount()); - assertEquals("Petropolis", resultSet.getString("CITY")); - assertTrue(resultSet.next()); - assertEquals("Ribeirao Preto", resultSet.getString("CITY")); - assertTrue(resultSet.next()); - assertEquals("Rio de Janeiro", resultSet.getString("CITY")); - assertTrue(resultSet.next()); - assertEquals("Sao Paulo", resultSet.getString("CITY")); - assertTrue(resultSet.next()); - assertEquals("Sorocaba", resultSet.getString("CITY")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with where clause, group by, having, and order by. - assertTrue((resultSet = driver.executeQuery("select CITY, COUNT(*) as total from PERSON where state = 'sp' or state = 'rj' group by CITY " + - "having total > 2 order by CITY")).next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(2, resultSet.getRowCount()); - assertEquals("Ribeirao Preto", resultSet.getString("CITY")); - assertEquals(3, resultSet.getInt("total")); - assertTrue(resultSet.next()); - assertEquals("Rio de Janeiro", resultSet.getString("CITY")); - assertEquals(4, resultSet.getInt("total")); - assertFalse(resultSet.next()); - resultSet.close(); - - // Select with aggregated functions and simple columns with group by and having clauses. - assertTrue((resultSet = driver.executeQuery("select CITY, state, COUNT(*) as total, min(age) as min_age, avg(age) as avg_age, " + - "sum(salary) as sum_salary, avg(salary) as avg_salary from PERSON group by state, CITY having min_age > 25 and sum_salary < 15000")).next()); - - // Asserts that the query worked by checking the values of each record. - assertEquals(3, resultSet.getRowCount()); - - // First record: Porto Alegre RS 2 36 49.0 10795.98 5397.9899999999998. - assertEquals(2, resultSet.getInt("total")); - assertEquals(36, resultSet.getShort("min_age")); - assertEquals("rs", resultSet.getString("state")); - assertEquals("Porto Alegre", resultSet.getString("CITY")); - assertEquals(49.01, resultSet.getDouble("avg_age"), 1e-2); - assertEquals(10795.98, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(5397.99, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Second record: Uruguaiana RS 1 26 26.0 4441.8800000000001 4441.8800000000001. - assertEquals(1, resultSet.getInt("total")); - assertEquals(26, resultSet.getShort("min_age")); - assertEquals("rs", resultSet.getString("state")); - assertEquals("Uruguaiana", resultSet.getString("CITY")); - assertEquals(26.0, resultSet.getDouble("avg_age"), 1e-1); - assertEquals(4441.88, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(4441.88, resultSet.getDouble("avg_salary"), 1e-2); - assertTrue(resultSet.next()); - - // Third record: Sorocaba SP 2 28 35.0 6234.3299999999999 3117.165. - assertEquals(2, resultSet.getInt("total")); - assertEquals(28, resultSet.getShort("min_age")); - assertEquals("sp", resultSet.getString("state")); - assertEquals("Sorocaba", resultSet.getString("CITY")); - assertEquals(35.0, resultSet.getDouble("avg_age"), 1e-1); - assertEquals(6234.33, resultSet.getDouble("sum_salary"), 1e-2); - assertEquals(3117.16, resultSet.getDouble("avg_salary"), 1e-2); - assertFalse(resultSet.next()); - resultSet.close(); - - // Tests a max in a table with many columns. - if (driver.exists("cliente")) - driver.executeUpdate("drop table cliente"); - driver.execute("create table cliente(clienteEmpresaId int not null, clienteId int not null, cpfCnpj varchar(14), tipoPessoa varchar(1), " + - "nome varchar(60), fantasia varchar(40), contato varchar(60), inscricaoRg varchar(18), consFinal varchar(1), limCredito double, " + - "endereco varchar(70), bairro varchar(40), cidadeId varchar(5), cep varchar(8), telefone varchar(14), email varchar(100), ramoId int, " + - "usuarioId int, usuarioEmpresaId int, sincronizado varchar(1), duplicatas double, prestadorId int, prestadorEmpresaId int, " + - "condPgtoEmpresaId int, condPgtoId int, numero varchar(10), descontoMax double)"); - assertFalse((resultSet = driver.prepareStatement(" SELECT max(clienteId) as id FROM cliente ").executeQuery()).first()); - resultSet.close(); - driver.closeAll(); - driver = AllTests.getInstance("Test"); - assertFalse((resultSet = driver.prepareStatement(" SELECT max(clienteId) as id FROM cliente ").executeQuery()).first()); - resultSet.close(); - driver.closeAll(); - } - - /** - * Executes a query and returns the total number of record. - * - * @param driver The connection with Litebase. - * @param sql The query. - * @return The number of records of the query. - */ - private int executeQuery(LitebaseConnection driver, String sql) - { - int count = 0; - output('\n' + sql); - ResultSet rs = driver.executeQuery(sql); - while (rs.next()) - count++; - rs.close(); - return count; - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestSourcePath.java b/LitebaseSDK/src/java/samples/sys/testcases/TestSourcePath.java deleted file mode 100644 index 497fe36c9f..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestSourcePath.java +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; -import totalcross.sys.*; -import totalcross.io.*; - -/** - * Tests the use of a different source path for Litebase. - */ -public class TestSourcePath extends TestCase -{ - /** - * Does the test with an especific path. - * - * @param tempPath - */ - private void doTest(String tempPath) - { - try - { - Settings.dataPath = tempPath; - LitebaseConnection driver = LitebaseConnection.getInstance("Test"); - if (driver.exists("twonames")) - driver.executeUpdate("drop table twonames"); - try - { - driver.execute("CREATE TABLE twonames (name1 CHAR(100), name2 CHAR(100))"); - driver.execute("CREATE INDEX IDX ON twonames(name1)"); - } catch (AlreadyCreatedException ace) - { - fail("Table already created. Exists didnt't work?"); - } - assertEquals(1,driver.executeUpdate("Insert into twonames values ('guich','michelle\\\\')")); - ResultSet resultSet = driver.executeQuery("SELECT * FROM twonames"); - assertTrue(resultSet.next()); - assertEquals("michelle\\", resultSet.getString(2)); - resultSet.close(); - - // Now closes the driver and tests again. - driver.closeAll(); - driver = LitebaseConnection.getInstance("Test"); - resultSet = driver.executeQuery("SELECT * FROM twonames"); - assertTrue(resultSet.next()); - resultSet.close(); - driver.closeAll(); - - // Checks if the files exist. - File f; - if (!(f = new File(tempPath + "Test-twonames.db", File.DONT_OPEN)).exists()) - fail("File doesn't exist. " + f.getPath()); - if (!(f = new File(tempPath + "Test-twonames.dbo", File.DONT_OPEN)).exists()) - fail("File doesn't exist. " + f.getPath()); - if (!(f = new File(tempPath + "Test-twonames$1.idk", File.DONT_OPEN)).exists()) - fail("File doesn't exist. " + f.getPath()); - } - catch (IOException exception) - { - fail(exception.getMessage()); - } - } - - /** - * The main test method. - */ - public void testRun() - { - String prevDataPath = Settings.dataPath; // Stores the previous data path. - doTest(Convert.appendPath(Settings.appPath, "temp/")); - - // Tests memory card and folders with stress. - try - { - String path = Convert.appendPath(Settings.appPath, "tempor�rio/"); - File file = new File(path); - file.createDir(); - file.close(); - - try - { - doTest(Convert.appendPath(File.getCardVolume().getPath(), "tempor�rio/")); - } - catch (IOException exception) - { - doTest(path); - } - catch (NullPointerException exception) - { - doTest(path); - } - } - catch (IOException exception) {} - - // Tests that the an exception will be thrown if a relative path is used on the device. - try - { - Settings.dataPath = "."; - LitebaseConnection.getInstance("Test"); - fail("1"); - } - catch (DriverException exception) {} - try - { - Settings.dataPath = "./temp"; - LitebaseConnection.getInstance("Test"); - fail("2"); - } - catch (DriverException exception) {} - try - { - Settings.dataPath = "/Litebase/../tables/"; - LitebaseConnection.getInstance("Test"); - fail("3"); - } - catch (DriverException exception) {} - - // The empty string is a valid dataPath. - try - { - Settings.dataPath = ""; - LitebaseConnection.getInstance("Test").closeAll(); - } - catch (DriverException exception) - { - fail("4"); - } - - // Null is a valid dataPath. - try - { - Settings.dataPath = null; - LitebaseConnection.getInstance("Test").closeAll(); - } - catch (DriverException exception) - { - fail("5"); - } - - // Invalid data paths. - try - { - Settings.dataPath = " "; - LitebaseConnection.getInstance("Test"); - fail("6"); - } - catch (DriverException exception) {} - try - { - Settings.dataPath = " "; - LitebaseConnection.getInstance("Test"); - fail("7"); - } - catch (DriverException exception) {} - - try - { - if (Settings.platform.equals(Settings.ANDROID)) - { - Settings.dataPath = "/Litebase_DBs/"; - LitebaseConnection.getInstance("Test"); - fail("9"); - } - } - catch (DriverException exception) {} - Settings.dataPath = prevDataPath; // Restores the data path. - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestTableRecovering.java b/LitebaseSDK/src/java/samples/sys/testcases/TestTableRecovering.java deleted file mode 100644 index dc53c95ea1..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestTableRecovering.java +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.io.*; -import totalcross.sys.*; -import totalcross.unit.TestCase; -import totalcross.util.Random; - -/** - * Tests LitebaseConnection.recoverTable(). - */ -public class TestTableRecovering extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - int record = 11, - i, - pos; - PreparedStatement prepared; - Random rand = new Random(); - File dbFile; - byte[] garbage = "garbage".getBytes(); - LitebaseConnection driver = AllTests.getInstance(); - - // Gets the files paths. - String tablePath = driver.getSourcePath() + "gqVX-person.db"; - driver.closeAll(); - - byte[] oneByte = new byte[1]; - byte[] blankBuffer = new byte[1024]; - String message; - - if (AllTests.useCrypto) - { - i = garbage.length; - while (--i >= 0) - garbage[i] ^= 0xAA; - } - - try - { - while (--record >= 0) // Repeats the test corrupting a different record, from the first (0) till after the last (10). - { - // Drops, creates and populates a table to be used in the test. - driver = AllTests.getInstance(); - if (driver.exists("person")) - driver.executeUpdate("drop table person"); - - try // The table does not exist yet. - { - driver.isTableProperlyClosed("person"); - fail("1"); - } - catch (DriverException exception) {} - - driver.execute("create table person (id int not null, name char(30) default 'Maria', cpf long not null, photo blob(2), gender char(1), " - + "birth datetime, primary key(id, cpf))"); - driver.execute("create index idx on person(rowid)"); - driver.execute("create index idx on person(name, gender, birth)"); - prepared = driver.prepareStatement("insert into person values(?, ?, ?, ?, ?, ?)"); - i = 10; - - assertTrue(driver.isTableProperlyClosed("person")); - - while (--i >= 0) - { - prepared.setInt(0, i); - prepared.setString(1, i + ""); - prepared.setLong(2, i); - prepared.setBlob(3, ("name" + i).getBytes()); - prepared.setString(4, (i % 2 == 0)? "F" : "M"); - prepared.setDateTime(5, new Time()); - prepared.executeUpdate(); - } - driver.executeUpdate("update person set id = 1"); - driver.purge("person"); - driver.closeAll(); // Must close the table before recovering it. - driver = AllTests.getInstance(); - - try // There is nothing to be recovered. - { - driver.recoverTable("peRson"); - fail("2"); - } - catch (DriverException exception) {} - dbFile = new File(tablePath, File.READ_WRITE); // The table is closed after recovering it. - - // Pretends that the table was not closed correctly. - dbFile.setPos(6); - dbFile.readBytes(oneByte, 0, 1); - - if (AllTests.useCrypto) - oneByte[0] ^= 0xAA; - oneByte[0] = (byte)(oneByte[0] & 2); - if (AllTests.useCrypto) - oneByte[0] ^= 0xAA; - dbFile.setPos(6); - dbFile.writeBytes(oneByte, 0, 1); - - // Corrupts the table database file. - if ((pos = 512 + 41 * record + rand.nextInt(33)) + 7 > dbFile.getSize()) - dbFile.setSize(pos + 7); - dbFile.setPos(pos); - dbFile.writeBytes(garbage); - dbFile.close(); - - try // It is not possible to open a corrupted table. - { - driver.executeQuery("select * from person"); - fail("3"); - } - catch (TableNotClosedException exception) {} - - assertFalse(driver.isTableProperlyClosed("person")); - assertEquals(record < 10, driver.recoverTable("person")); // If the corruption occurs after the last record, nothing needs to be recovered. - assertTrue(driver.isTableProperlyClosed("person")); - - ResultSet resultSet = driver.executeQuery("select * from person"); - - // If the corruption occurs after the last records, all the records are available. - assertEquals(record < 10? 9 : 10, resultSet.getRowCount()); - - resultSet.close(); - - // Posix platforms share file handlers. - if (!(Settings.platform.equals(Settings.ANDROID) || Settings.platform.equals(Settings.IPHONE))) - { - assertTrue(driver.isTableProperlyClosed("person")); - try // Table being used. - { - driver.recoverTable("peRson"); - fail("4"); - } - catch (DriverException exception) {} - try // Table being used. - { - driver.convert("peRson"); - fail("5"); - } - catch (DriverException exception) {} - } - - // Convert must fail because the table version used is the current one. - driver.closeAll(); - driver = AllTests.getInstance(); - try - { - driver.convert("person"); - fail("6"); - } - catch (DriverException exception) {} - - driver.executeUpdate("drop table person"); - - new File(tablePath, File.CREATE_EMPTY).close(); - new File(tablePath + 'o', File.CREATE_EMPTY).close(); - try // Empty file: table corrupted. - { - driver.recoverTable("person"); - fail("7"); - } - catch (DriverException exception) - { - assertTrue((message = exception.getMessage()).indexOf("corrupted") != -1 || message.indexOf("format") != -1 - || message.indexOf("read") != -1); - } - try // Empty file: table corrupted. - { - driver.recoverTable("person"); - fail("8"); - } - catch (DriverException exception) - { - assertTrue((message = exception.getMessage()).indexOf("corrupted") != -1 || message.indexOf("format") != -1 - || message.indexOf("read") != -1); - } - - File file = new File(tablePath, File.CREATE_EMPTY); - file.setSize(1024); - file.writeBytes(blankBuffer); - file.close(); - assertFalse(driver.isTableProperlyClosed("person")); - try // Blank file: table corrupted. - { - driver.recoverTable("person"); - fail("9"); - } - catch (DriverException exception) - { - assertTrue((message = exception.getMessage()).indexOf("corrupted") != -1 || message.indexOf("format") != -1); - } - - // Erases the file. - file = new File(tablePath, File.DONT_OPEN); - file.delete(); - - driver.closeAll(); - } - } - catch (IOException exception) - { - fail("10"); - } - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestThread.java b/LitebaseSDK/src/java/samples/sys/testcases/TestThread.java deleted file mode 100644 index 83c98ec7cd..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestThread.java +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.sys.*; -import totalcross.unit.TestCase; - -/** - * An internal class that is used to tests threads with Litebase. - */ -final class LBThread implements Runnable -{ - /** - * The thread. - */ - Thread t; - - /** - * A table id: 1 or 2. - */ - int tableId; - - /** - * Indicates if the thread finished correctly. - */ - boolean finishedGracefully; - - /** - * Indicates that the thread must only sleep when it is running on a computer. - */ - final static boolean doSleep = Settings.platform.equals(Settings.WIN32) || Settings.platform.equals(Settings.LINUX) || Settings.onJavaSE; - - /** - * The thread run() method. - */ - public void run() - { - try - { - // Opens the connection with Litebase: one for each thread. - LitebaseConnection conn = AllTests.getInstance("Test"); - if (doSleep) { - Thread.yield(); - } - - // Creates the table. - if (conn.exists("person" + tableId)) - conn.executeUpdate("drop table person" + tableId); - conn.execute("create table person" + tableId + " (id int primary key, name char(30))"); - if (doSleep) { - Thread.yield(); - } - - // Empties the table. - conn.executeUpdate("delete from person" + tableId); - - // Populates the table. - int i = -1; - PreparedStatement stmt = conn.prepareStatement("insert into person" + tableId + " (id,name) values(?, ?)"); - while (++i < 100) - { - if (i == 50 && doSleep) { - Thread.yield(); - } - stmt.setInt(0, i); - stmt.setString(1, "Name " + i); - stmt.executeUpdate(); - } - if (doSleep) { - Thread.yield(); - } - - // Searches the entire table to see if it was built correctly. - ResultSet resultSet = conn.executeQuery("select * from person" + tableId); - int rowCount = resultSet.getRowCount(); - if (rowCount != 100) - throw new Exception(rowCount + " != 100"); - String name; - i = -1; - while (++i < 100) - { - resultSet.next(); - name = resultSet.getString("Name"); - if (!name.equals("Name " + i)) - throw new Exception(name + " != Name " + i); - if (doSleep) { - Thread.yield(); - } - } - - // Searches the table using the index. - resultSet = conn.executeQuery("select * from person" + tableId + " where id >= 50"); - rowCount = resultSet.getRowCount(); - resultSet.close(); - if (rowCount != 50) - throw new Exception(rowCount + " != 50"); - if (doSleep) { - Thread.yield(); - } - conn.closeAll(); - - LitebaseConnection.setLogger(LitebaseConnection.getLogger()); // Tests the logger. - finishedGracefully = true; // The thread finished correctly. - } - catch (Exception exception) - { - exception.printStackTrace(); // Shows the error message. - throw new RuntimeException(exception.getMessage()); - } - } -} - -/** - * Tests Litebase with threads. - */ -public class TestThread extends TestCase -{ - /** - * The main test method. - */ - public void testRun() - { - LBThread test1, - test2; - test1 = new LBThread(); - test1.tableId = 1; - test1.t = new Thread(test1); - test1.t.start(); - test2 = new LBThread(); - test2.tableId = 2; - test2.t = new Thread(test2); - test2.t.start(); - while (test1.t.isAlive() || test2.t.isAlive()) - Vm.sleep(10); - output("Thread 1 " + (test1.finishedGracefully? "finished" : "aborted")); - output("Thread 2 " + (test2.finishedGracefully? "finished" : "aborted")); - - if (!test1.finishedGracefully) - fail("thread 1 aborted"); - if (!test2.finishedGracefully) - fail("thread 2 aborted"); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestVirtualRecords.java b/LitebaseSDK/src/java/samples/sys/testcases/TestVirtualRecords.java deleted file mode 100644 index b8a11d4324..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestVirtualRecords.java +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * This tests if the load factor growth is being stored correctly. - */ -public class TestVirtualRecords extends TestCase -{ - /** - * Tests if the select results are as expected. - * - * @param driver The connection with Litebase. - * @param countAssert The expected number of returned rows. - */ - private void doSelect(LitebaseConnection driver, int countAssert) - { - ResultSet resultSet = driver.executeQuery(" select name from virtrecs"); - int count = 0; - - resultSet.afterLast(); - while (resultSet.prev()) - if (count++ % 10 == 0) - assertEquals("Name" + (countAssert - count), new String(resultSet.getChars(1))); - assertEquals(countAssert, resultSet.getRowCount()); - resultSet.close(); - assertEquals(countAssert, count); - } - - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - try - { - driver.executeUpdate(" drop table virtrecs"); - } - catch (DriverException de) {} - - try - { - // Huge records! - driver.execute(" CREATE TABLE virtrecs (NAME CHAR(500) NOCASE, ADDRESS char(700))"); - driver.execute(" create index idxname on virtrecs(name)"); - } - catch (AlreadyCreatedException ace) {} - - try // There can't be two indices for the same column. - { - driver.execute(" create index idxname on virtrecs(name)"); - fail(); - } - catch (AlreadyCreatedException ace) {} - - // Tests the select on the empty table. - ResultSet resultSet = driver.executeQuery("select name from virtrecs"); - assertEquals(0, resultSet.getRowCount()); - assertFalse(resultSet.next()); - resultSet.close(); - - // Populates the table. - driver.setRowInc("virtrecs", 100); - int i = -1; - while (++ i < 100) - assertEquals(1, driver.executeUpdate(" INSERT INTO virtrecs VALUES ('Name" + i + "','Addr" + i + "')")); - driver.setRowInc("virtrecs", -1); - - // Does a query that returns no results. - assertEquals(0, (resultSet = driver.executeQuery("select name from virtrecs where name = 'xxx'")).getRowCount()); - - // The result set is kept open. - doSelect(driver, 100); - driver.closeAll(); - - // Tries again. - driver = AllTests.getInstance("Test"); - doSelect(driver, 100); - - // Tries to insert data after closeAll(); - assertEquals(1, driver.executeUpdate("INSERT INTO virtrecs VALUES ('Name100', 'Addr100')")); - doSelect(driver, 101); - driver.closeAll(); - - // Tries again. - driver = AllTests.getInstance("Test"); - doSelect(driver, 101); - - // Closes every thing. - resultSet.close(); - - // Very big identifiers. - driver.executeUpdate("drop table virtrecs"); - driver.execute("create table virtrecs (namenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamename char(10), id double)"); - driver.executeUpdate("insert into virtrecs values ('12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890', 0)"); - try - { - driver.executeUpdate("update virtrecs set id = 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"); - } - catch (SQLParseException exception) {} - driver.closeAll(); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Basic.java b/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Basic.java deleted file mode 100644 index c6ff0741c8..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Basic.java +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests simple where clauses without indices. - */ -public class TestWhereClause_Basic extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - try - { - driver.executeUpdate("drop table PERSON"); - } - catch (DriverException exception) {} // Table not found. - - String[] insertRows = // Rows to insert. - { - "insert into PERSON values ('guilherme', 'hazan', 4400.50, 3400.80, 10, 6)", - "insert into PERSON values ('raimundo', 'correa', 3400.50, 3400.50, 11, 26)", - "insert into PERSON values ('ricardo', 'zorba', 10400.50, 5000.20, 23, 23)", - "insert into PERSON values ('cher', 'cher', 1000.50, 3400.50, 4, 3)", - "insert into PERSON values ('lero', 'lero', 2001.34, 1000.35, 2, 6)", - "insert into PERSON values ('zico', 'mengao', 1000.51, 1000.51, 12, 0)", - "insert into PERSON values ('roberto', 'dinamite', 2222.51, 1.21, 10, 10)", - "insert into PERSON values ('socrates', 'sampaio', 1111.50, 1111.50, 7, 11)", - "insert into PERSON values ('paulo', 'falcao', 2800.04, 1.21, 15, 12)", - - // with empty and negative values - "insert into PERSON values ('leo', '', 2.50, 3400.50, -4, 5)" }; - int numRows = insertRows.length; - - // Creates table. - driver.execute("create table PERSON (FIRST_NAME CHAR(30), LAST_NAME CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, YEARS_EXP_JAVA INT, " - + "YEARS_EXP_C INT )"); - - // Inserts rows. - int i = -1; - while (++i < numRows) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - // Runs SQL query to test the where clause parsing and evaluation. - // No where clause - assertEquals(numRows, executeQuery(driver, "select * from PERSON")); - - // Empty strings - assertEquals(1, executeQuery(driver, "select * from PERSON where last_name= ''")); - - // Parameter order test. - ResultSet resultSet = driver.executeQuery("select first_name, salary_cur from person where rowid = 2"); - assertTrue(resultSet.first()); - assertEquals("raimundo", resultSet.getString(1)); - resultSet.close(); - assertTrue((resultSet = driver.executeQuery("select salary_cur, first_name from person where rowid = 2")).first()); - assertEquals("raimundo", resultSet.getString(2)); - resultSet.close(); - assertTrue((resultSet = driver.executeQuery("select * from person where rowid = 2")).first()); - - // guich@503_10: asserts a bug just corrected. - assertEquals("raimundo", resultSet.getString(1)); - - // Integer comparison - int greatThanCount = 4; - int equalCount = 2; - - // Simple query, with integer comparison >=. - assertEquals(greatThanCount + equalCount, executeQuery(driver, "select * from PERSON where years_exp_java >= 10")); - - // Simple query, with integer comparison >. - assertEquals(greatThanCount, executeQuery(driver, "select * from PERSON where years_exp_java > 10")); - - // Simple query, with integer comparison =. - assertEquals(equalCount, executeQuery(driver, "select * from PERSON where years_exp_java = 10")); - - // Simple query, with imediate negative numbers. - assertEquals(1, executeQuery(driver, "select * from PERSON where years_exp_java = -4")); - - // Simple query, with imediate positive numbers. - assertEquals(equalCount, executeQuery(driver, "select * from PERSON where years_exp_java = +10")); - - // Simple query, with integer comparison <>. - assertEquals(numRows - equalCount, executeQuery(driver, "select * from PERSON where years_exp_java <> 10")); - - // Simple query, with integer comparison !=. - assertEquals(numRows - equalCount, executeQuery(driver, "select * from PERSON where years_exp_java != 10")); - - // Simple query, with integer comparison <. - assertEquals(numRows - equalCount - greatThanCount, executeQuery(driver, "select * from PERSON where years_exp_java < 10")); - - // Simple query, with integer comparison <=. - assertEquals(numRows - greatThanCount, executeQuery(driver, "select * from PERSON where years_exp_java <= 10")); - - // Double comparison. - greatThanCount = 2; - equalCount = 3; - - // Simple query, with double comparison >=. - assertEquals(greatThanCount + equalCount, executeQuery(driver, "select * from PERSON where salary_prev >= 3400.50")); - - // Simple query, with double comparison >. - assertEquals(greatThanCount, executeQuery(driver, "select * from PERSON where salary_prev > 3400.50")); - - // Simple query, with double comparison =. - assertEquals(equalCount, executeQuery(driver, "select * from PERSON where salary_prev = 3400.50")); - - // Simple query, with double comparison <>. - assertEquals(numRows - equalCount, executeQuery(driver, "select * from PERSON where salary_prev <> 3400.50")); - - // Simple query, with double comparison !=. - assertEquals(numRows - equalCount, executeQuery(driver, "select * from PERSON where salary_prev != 3400.50")); - - // Simple query, with double comparison <. - assertEquals(numRows - equalCount - greatThanCount, executeQuery(driver, "select * from PERSON where salary_prev < 3400.50")); - - // Simple query, with double comparison <=. - assertEquals(numRows - greatThanCount, executeQuery(driver, "select * from PERSON where salary_prev <= 3400.50")); - - // String comparison. - greatThanCount = 4; - equalCount = 1; - - // Simple query, with string comparison >=. - assertEquals(greatThanCount + equalCount, executeQuery(driver, "select * from PERSON where FIRST_NAME >= 'raimundo'")); - - // Simple query, with string comparison >. - assertEquals(greatThanCount, executeQuery(driver, "select * from PERSON where FIRST_NAME > 'raimundo'")); - - // Simple query, with string comparison =. - assertEquals(equalCount, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'raimundo'")); - - // Simple query, with string comparison <>. - assertEquals(numRows - equalCount, executeQuery(driver, "select * from PERSON where FIRST_NAME <> 'raimundo'")); - - // Simple query, with string comparison !=. - assertEquals(numRows - equalCount, executeQuery(driver, "select * from PERSON where FIRST_NAME != 'raimundo'")); - - // Simple query, with string comparison <. - assertEquals(numRows - equalCount - greatThanCount, executeQuery(driver, "select * from PERSON where FIRST_NAME < 'raimundo'")); - - // Simple query, with string comparison <=. - assertEquals(numRows - greatThanCount, executeQuery(driver, "select * from PERSON where FIRST_NAME <= 'raimundo'")); - - // String matching. - assertEquals(3, executeQuery(driver, "select * from PERSON where FIRST_NAME like 'r%'")); // Starts with. - assertEquals(2, executeQuery(driver, "select * from PERSON where last_name like '%ao'")); // Ends with. - assertEquals(2, executeQuery(driver, "select * from PERSON where last_name like '%or%'")); // Contains. - assertEquals(4, executeQuery(driver, "select * from PERSON where last_name like '%r%'")); // Contains. - assertEquals(8, executeQuery(driver, "select * from PERSON where last_name not like '%or%'")); // Not match. - assertEquals(4, executeQuery(driver, "select * from PERSON where last_name not like '%o%'")); // Not match. - assertEquals(1, executeQuery(driver, "select * from PERSON where last_name like 'din%ite'")); // Starts and ends with. - assertEquals(1, executeQuery(driver, "select * from PERSON where last_name like 'sampaio'")); // LIKE as equals. - - // Field comparison. - // Simple query, with integer field comparison >=. - assertEquals(6, executeQuery(driver, "select * from PERSON where years_exp_java >= years_exp_c")); - - // Simple query, with integer field comparison >. - assertEquals(4, executeQuery(driver, "select * from PERSON where years_exp_java > years_exp_c")); - - // Simple query, with string field comparison =. - assertEquals(2, executeQuery(driver, "select * from PERSON where FIRST_NAME = last_name")); - - // Simple query, with string field comparison <>. - assertEquals(8, executeQuery(driver, "select * from PERSON where FIRST_NAME <> last_name")); - - // Simple query, with string field comparison !=. - assertEquals(8, executeQuery(driver, "select * from PERSON where FIRST_NAME != last_name")); - - // Simple query, with double field comparison <. - assertEquals(5, executeQuery(driver, "select * from PERSON where salary_prev < salary_cur")); - - // Simple query, with double field comparison <=. - assertEquals(8, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur")); - - // Complex queries. - assertEquals(2, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur and last_name like '%or%'")); - assertEquals(8, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur or last_name like '%or%'")); - assertEquals(4, executeQuery(driver, "select * from PERSON where salary_prev <= salary_cur and not years_exp_c > 10")); - assertEquals(6, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 and years_exp_java >= 10) or FIRST_NAME > 'r'")); - assertEquals(4, executeQuery(driver, "select * from PERSON where salary_prev < 3000 and (years_exp_java >= 10 or FIRST_NAME > 'r')")); - assertEquals(2, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 or not years_exp_c >= years_exp_java) " - + "and not (years_exp_java >= 10 or FIRST_NAME > 'r')")); - assertEquals(8, executeQuery(driver, "select * from PERSON where salary_prev < 3000 or ((not years_exp_c >= years_exp_java) " - + "and not years_exp_java >= 10) or FIRST_NAME > 'r'")); - - // Complex queries with update. - assertEquals(8, driver.executeUpdate("update PERSON set FIRST_NAME = 'x' where salary_prev < 3000 or ((not years_exp_c >= years_exp_java) " - + "and not years_exp_java >= 10) or FIRST_NAME > 'r'")); - assertEquals(8, driver.executeUpdate("delete PERSON where salary_prev < 3000 or ((not years_exp_c >= years_exp_java) " - + "and not years_exp_java >= 10) or FIRST_NAME > 'r'")); - - // Updates the whole table. - assertEquals(2, driver.executeUpdate("update PERSON set FIRST_NAME = 'Michelle'")); - driver.closeAll(); - } - - /** - * Tests a query. - * - * @param driver The connection with Litebase. - * @param sql The query to be executed. - * @return The number of rows returned by the query. - */ - private int executeQuery(LitebaseConnection driver, String sql) - { - int count = 0; - ResultSet resultSet = driver.executeQuery(sql); - while (resultSet.next()) - count++; - resultSet.close(); - return count; - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Caseless.java b/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Caseless.java deleted file mode 100644 index 4df784a8dd..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Caseless.java +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests where clauses taking string cases into consideration. - */ -public class TestWhereClause_Caseless extends TestCase -{ - /** - * The main method of the test. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - try - { - driver.executeUpdate("drop table PERSON"); - } - catch (DriverException pe) {} // Table not found. - try - { - driver.executeUpdate("drop table PERSON2"); - } - catch (DriverException pe) {} // Table not found. - - // Rows to insert. - String [] insertRows = - { - "insert into PERSON values ('guilherme', 'HAZAN', 'Rio de Janeiro', 4400.50, 3400.80, 10, 6)", - "insert into PERSON values ('raimundo', 'correa', 'Fortaleza', 3400.50, 3400.50, 11, 26)", - "insert into PERSON values ('ricardo', 'souza', 'NATAL', 10400.50, 5000.20, 23, 23)", - "insert into PERSON values ('cher', 'cher', 'Fortaleza', 1000.50, 3400.50, 4, 3)", - "insert into PERSON values ('maria', 'jose', 'Paraty', 2001.34, 1000.35, 2, 6)", - "insert into PERSON values ('ZICO', 'ZICO', 'Ouro Preto', 1000.51, 1000.51, 12, 0)", - "insert into PERSON values ('roberto', 'Dinamite', 'Rio de Janeiro', 2222.51, 1.21, 10, 10)", - "insert into PERSON values ('socrates', 'sampaio', 'Porto Seguro', 1111.50, 1111.50, 7, 11)", - "insert into PERSON values ('paulo', 'dinamite', 'RIO DE JANEIRO', 2800.04, 1.21, 15, 12)", - "insert into PERSON values ('leo', 'Souza', 'Natal', 2.50, 3400.50, 4, 5)", - "insert into PERSON values ('maria', 'tatu', 'Foz do Igua�u', 4400.50, 3400.80, 10, 6)", - "insert into PERSON values ('guilherme', 'renato', 'Porto Seguro', 3400.50, 3400.50, 11, 26)", - "insert into PERSON values ('zanata', 'dinamite', 'Florianopolis', 10400.50, 5000.20, 23, 23)", - "insert into PERSON values ('roberto', 'DINAMITE', 'Natal', 1000.50, 3400.50, 4, 3)", - "insert into PERSON values ('maria', 'MARIA', 'Fortaleza', 2001.34, 1000.35, 2, 6)", - "insert into PERSON values ('MARIA', 'severina', 'Porto Seguro', 1000.51, 1000.51, 12, 0)", - "insert into PERSON values ('roberto', 'carlos', 'Ouro PRETO', 2222.51, 1.21, 10, 10)", - "insert into PERSON values ('paulo', 'JOSE', 'Porto Seguro', 1111.50, 1111.50, 7, 11)", - "insert into PERSON values ('cher', 'cher', 'rio de janeiro', 2800.04, 1.21, 15, 12)", - "insert into PERSON values ('maria', 'joao', 'Rio de Janeiro', 2.50, 3400.50, 4, 5)" - }; - int numRows = insertRows.length; - - // Caseless strings. - // Creates table. - driver.execute("create table PERSON (FIRST_NAME CHAR(30) NOCASE, LAST_NAME CHAR(40) NOCASE, CITY CHAR(30) NOCASE, SALARY_CUR DOUBLE, " - + "SALARY_PREV DOUBLE, YEARS_EXP_JAVA INT, YEARS_EXP_C INT )"); - - // Inserts rows. - int i = numRows; - while (-- i >= 0) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - testQueriesCaseless(driver); // Case-insensitive queries without index. - - // Case-insensitive queries with index. - driver.execute("create index idx_name on PERSON(FIRST_NAME)"); - driver.execute("create index idx_city on PERSON(city)"); - driver.execute("create index idx_exp_C on PERSON(years_exp_C)"); - - testQueriesCaseless(driver); // Repeats queries. - - // Case sensitive. - // Creates a new table. - driver.executeUpdate("drop table person"); - driver.execute("create table PERSON (FIRST_NAME CHAR(30), LAST_NAME CHAR(40), CITY CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, " - + "YEARS_EXP_JAVA INT, YEARS_EXP_C INT )"); - // Insert rows - i = numRows; - while (-- i >= 0) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - testQueriesCase(driver); // Case-sensitive queries without index. - - // Case-sensitive queries with index. - driver.execute("create index idx_name on PERSON(FIRST_NAME)"); - driver.execute("create index idx_city on PERSON(city)"); - driver.execute("create index idx_exp_C on PERSON(years_exp_C)"); - - testQueriesCase(driver); // Repeats queries. - - driver.closeAll(); - } - - /** - * Tests a query. - * - * @param driver The connection with Litebase. - * @param sql The query to be executed. - * @return The number of rows returned by the query. - */ - private int executeQuery(LitebaseConnection driver, String sql) - { - int count = 0; - ResultSet resultSet = driver.executeQuery(sql); - while (resultSet.next()) - count++; - resultSet.close(); - return count; - } - - /** - * Queries the table with caseless strings. - * - * @param driver The connection with Litebase. - */ - private void testQueriesCaseless(LitebaseConnection driver) - { - // Exact key search. - assertEquals(5, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria'")); - assertEquals(15, executeQuery(driver, "select * from PERSON where FIRST_NAME != 'maria'")); - - // Exact search with OR. - assertEquals(9, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' or LAST_NAME = 'dinamite'")); - assertEquals(5, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'roberto' or city = 'NATAL'")); - - // Exact search with AND. - assertEquals(1, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and city = 'Paraty'")); - assertEquals(3, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and YEARS_EXP_C = 6")); - - // Non-exact search with OR. - assertEquals(6, executeQuery(driver, "select * from PERSON where city = 'Ouro PRETO' or SALARY_CUR > 10000 or years_exp_C = 12")); - assertEquals(10, executeQuery(driver, "select * from PERSON where last_name = 'Jose' or years_exp_Java > 10 or years_exp_C > 18")); - - // Non-exact search with AND. - assertEquals(2, executeQuery(driver, "select * from PERSON where city = 'rio de Janeiro' and years_exp_Java > 10 and years_exp_C > 3")); - - // Complex queries. - assertEquals(3, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 and years_exp_C = 10) or city = 'ouro preto' " - + "or first_name = 'zico'")); - assertEquals(9, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' or (SALARY_CUR > 3000 and years_exp_C = 6) " - + "or city = 'natal'")); - assertEquals(5, executeQuery(driver, "select * from PERSON where city = 'Rio de Janeiro' and (years_exp_Java > 10 " - + "or years_exp_C > 3)")); - assertEquals(8, executeQuery(driver, "select * from PERSON where first_name = last_name or last_name = 'dinamite'")); - - // Group by. - assertEquals(8, executeQuery(driver, "select city from PERSON group by city")); - } - - /** - * Queries the table taking the strings case into consideration. - * - * @param driver The connection with Litebase. - */ - private void testQueriesCase(LitebaseConnection driver) - { - // Exact key search. - assertEquals(4, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria'")); - assertEquals(16, executeQuery(driver, "select * from PERSON where FIRST_NAME != 'maria'")); - - // Exact search with OR. - assertEquals(6, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' or LAST_NAME = 'dinamite'")); - assertEquals(4, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'roberto' or city = 'NATAL'")); - - // Exact search with AND. - assertEquals(1, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and city = 'Paraty'")); - assertEquals(3, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and YEARS_EXP_C = 6")); - - // Non-exact search with OR. - assertEquals(5, executeQuery(driver, "select * from PERSON where city = 'Ouro PRETO' or SALARY_CUR > 10000 or years_exp_C = 12")); - assertEquals(8, executeQuery(driver, "select * from PERSON where last_name = 'Jose' or years_exp_Java > 10 or years_exp_C > 18")); - - // Non-exact search with AND. - assertEquals(0, executeQuery(driver, "select * from PERSON where city = 'rio de Janeiro' and years_exp_Java > 10 and years_exp_C > 3")); - - // Complex queries. - assertEquals(2, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 and years_exp_C = 10) or city = 'ouro preto' " - + "or first_name = 'zico'")); - assertEquals(5, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' or (SALARY_CUR > 3000 and years_exp_C = 6) " - + "or city = 'natal'")); - assertEquals(3, executeQuery(driver, "select * from PERSON where city = 'Rio de Janeiro' and (years_exp_Java > 10 " - + "or years_exp_C > 3)")); - assertEquals(5, executeQuery(driver, "select * from PERSON where first_name = last_name or last_name = 'dinamite'")); - - // Group by. - assertEquals(12, executeQuery(driver, "select city from PERSON group by city")); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Indexes.java b/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Indexes.java deleted file mode 100644 index 5b44a42e69..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/TestWhereClause_Indexes.java +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -package samples.sys.testcases; - -import litebase.*; -import totalcross.unit.*; - -/** - * Tests where clauses with and without indices. - */ -public class TestWhereClause_Indexes extends TestCase -{ - /** - * The main test method. - */ - public void testRun() - { - LitebaseConnection driver = AllTests.getInstance("Test"); - - String[] insertRows = // Rows to insert. - { - "insert into PERSON values ('null', 'null', 0.0, 0.0, 0, 0)", - "insert into PERSON values ('guilherme', 'Rio de Janeiro', 4400.50, 3400.80, 10, 6)", - "insert into PERSON values ('raimundo', 'Fortaleza', 3400.50, 3400.50, 11, 26)", - "insert into PERSON values ('ricardo', 'Natal', 10400.50, 5000.20, 23, 23)", - "insert into PERSON values ('cher', 'Fortaleza', 1000.50, 3400.50, 4, 3)", - "insert into PERSON values ('maria', 'Paraty', 2001.34, 1000.35, 2, 6)", - "insert into PERSON values ('zico', 'Ouro Preto', 1000.51, 1000.51, 12, 0)", - "insert into PERSON values ('roberto', 'Rio de Janeiro', 2222.51, 1.21, 10, 10)", - "insert into PERSON values ('socrates', 'Porto Seguro', 1111.50, 1111.50, 7, 11)", - "insert into PERSON values ('paulo', 'Rio de Janeiro', 2800.04, 1.21, 15, 12)", - "insert into PERSON values ('leo', 'Natal', 2.50, 3400.50, 4, 5)", - "insert into PERSON values ('maria', 'Foz do Igua�u', 4400.50, 3400.80, 10, 6)", - "insert into PERSON values ('guilherme', 'Porto Seguro', 3400.50, 3400.50, 11, 26)", - "insert into PERSON values ('zanata', 'Florianopolis', 10400.50, 5000.20, 23, 23)", - "insert into PERSON values ('roberto', 'Natal', 1000.50, 3400.50, 4, 3)", - "insert into PERSON values ('maria', 'Fortaleza', 2001.34, 1000.35, 2, 6)", - "insert into PERSON values ('maria', 'Porto Seguro', 1000.51, 1000.51, 12, 0)", - "insert into PERSON values ('roberto', 'Ouro Preto', 2222.51, 1.21, 10, 10)", - "insert into PERSON values ('paulo', 'Porto Seguro', 1111.50, 1111.50, 7, 11)", - "insert into PERSON values ('cher', 'Rio de Janeiro', 2800.04, 1.21, 15, 12)", - "insert into PERSON values ('maria', 'Rio de Janeiro', 2.50, 3400.50, 4, 5)", - "insert into PERSON values (null, null, null, null, null, null)" - }; - int numRows = insertRows.length; - - try - { - driver.executeUpdate("drop table PERSON"); - } - catch (DriverException exception) {} // Table not found. - - // Creates the table. - driver.execute("create table PERSON (FIRST_NAME CHAR(30), CITY CHAR(30), SALARY_CUR DOUBLE, SALARY_PREV DOUBLE, YEARS_EXP_JAVA INT, " - + "YEARS_EXP_C INT )"); - - // Inserts rows. - int i = numRows; - while (--i >= 0) - assertEquals(1, driver.executeUpdate(insertRows[i])); - - // Run SQL queries to test the WHERE clause evaluation using indexes usage. - testQueries(driver); // Queries without index. - - // Queries with index. - // Creates the indices. - driver.execute("create index idx_name on PERSON(FIRST_NAME)"); - driver.execute("create index idx_city on PERSON(city)"); - driver.execute("create index idx_exp_C on PERSON(years_exp_C)"); - - try // A primary key can't have null. - { - driver.executeUpdate("alter table person add primary key (SALARY_CUR)"); - fail(); - } - catch (DriverException exception) {} - - testQueries(driver); // Repeats queries. - - // A more realistic example. - try - { - driver.executeUpdate("drop table parecer"); - } - catch (DriverException exception) {} // Table not found. - - // Creates the tables and indices. - driver.execute("create table parecer(numero_registro int primary key, nome_obra char(08), fase_obra char(04), Codigo_Etapa short, " - + "numero_ord_serv int, codigo_atividade short, quant_atividade Double, codigo_empreiteiro char(08), data_prevista_ini char(10), " - + "data_prevista_fim char(10), data_vistoria char(10), check_list char(25), Ident_usuario char(10)) "); - driver.execute("Create index idxParObra on parecer(nome_obra)"); - driver.execute("Create index idxParFase on parecer(Fase_obra)"); - driver.execute("Create index idxParOs on parecer(numero_ord_serv)"); - driver.execute("Create index idxParAtiv on parecer(Codigo_Atividade)"); - driver.execute("Create index idxParEtp on parecer(Codigo_Etapa)"); - - // Inserts the rows. - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(1, 'OBRA 01', 'CA26', 1, 1, 3, 1, '1', '01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(2, 'OBRA 01', 'CA27', 1, 100, 2, 1, '1', '01/01/2006', '01/01/2006' ,'01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(3, 'OBRA 01', 'CA27', 1, 2, 3, 1, '1', '01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(4, 'OBRA 01', 'CA30', 1, 4, 1, 1, '1', '01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(5, 'OBRA 01', 'CA30', 1, 10, 2, 1, '1'," - + "'01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(6, 'OBRA 01', 'CA30', 1, 11, 3, 1, '1', '01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(7, 'OBRA 02', 'CA30', 1, 12, 1, 1, '1', '01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - driver.executeUpdate("Insert into parecer(numero_registro, nome_obra, fase_obra, Codigo_Etapa, numero_ord_serv , codigo_atividade , " - + "quant_atividade , codigo_empreiteiro , data_prevista_ini, data_prevista_fim, data_vistoria, check_list, Ident_usuario) " - + "Values(8, 'OBRA 02', 'CA30', 1, 13, 3, 1, '1', '01/01/2006', '01/01/2006', '01/01/2006', 'BBBBBBBBBBBBBBBBBBBBB', '')"); - - // Does a query to test the indices. - ResultSet resultSet = driver.executeQuery("Select codigo_Empreiteiro, Codigo_Atividade, Numero_Ord_Serv, Data_Prevista_Ini, Data_Prevista_Fim," - + " Data_Vistoria From parecer Where (numero_ord_serv > 0) And (Codigo_Etapa = 001) And (Nome_obra = 'OBRA 01') And (Fase_Obra = 'CA27')"); - assertEquals(2, resultSet.getRowCount()); - resultSet.close(); - - // Another realistic sample. - if (driver.exists("visitaDetalhe")) - driver.executeUpdate("drop table visitaDetalhe"); - driver.execute("create table visitaDetalhe ( codigoVendedor char( 6 ), dataRoteiro char( 8 ), codigoCliente char( 8 ), status char( 2 ), " - + "dataRemarcacao char( 8 ), sequencia char( 3 ), estagio int, acoes char( 30 ), obs1 char( 30 ), obs2 char( 30 ), proximosPassos char( 30 ), " - + "contato1 char( 15 ), contato2 char( 15 ), codigoContato1 char( 6 ), codigoContato2 char( 6 ), ddd1 char( 3 ), telefone1 char( 10 ), " - + "ddd2 char( 3 ), telefone2 char( 10 ), potencial int, fechamento char( 3 ), programado char( 1 ), email char( 40 ), codSegmento char( 4 ) )"); - driver.execute("create index idx_dataRoteiro on visitaDetalhe(dataRoteiro)"); - assertEquals(0, driver.executeUpdate("delete visitaDetalhe where dataRoteiro <> '20110211'")); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00000301','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00000401','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00000401','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00001501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00001501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00001701','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00002501','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00002501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00002501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00003001','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - - assertEquals(10, driver.executeUpdate("delete visitaDetalhe where dataRoteiro <> '20110211'")); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00000301','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00000401','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00000401','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00001501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00001501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00001701','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00002501','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00002501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00002501','03','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - driver.executeUpdate("insert into visitaDetalhe values ( '700001','20101001','00003001','00','00000000','000',0,'','','','','','','','','',''," - + "'','',0,'---','S','','' )"); - assertEquals(10, driver.executeUpdate("delete visitaDetalhe where dataRoteiro <> '20110211'")); - - try - { - driver.executeUpdate("drop table PERSON"); - } - catch (DriverException exception) {} // Table not found. - - // Inserts 0 in the index and tries to find the record when deleting it. - driver.execute("create table person (id int)"); - driver.execute("create index idx on person (id)"); - assertEquals(1, driver.executeUpdate("insert into person values (0)")); - assertEquals(1, driver.executeUpdate("delete from person p where p.rowid = 1")); - assertEquals(0, driver.getRowCount("person")); - assertEquals(1, driver.getRowCountDeleted("person")); - assertEquals(0, driver.executeUpdate("delete from person")); - assertEquals(0, driver.getRowCount("person")); - assertEquals(1, driver.getRowCountDeleted("person")); - assertEquals(1, driver.executeUpdate("insert into person values (0)")); - assertEquals(1, driver.executeUpdate("insert into person values (null)")); - assertEquals(1, driver.executeUpdate("delete from person p where p.id is not null")); - assertEquals(2, driver.getRowCountDeleted("person")); - assertEquals(1, driver.getRowCount("person")); - assertEquals(1, driver.executeUpdate("delete from person p where p.id is null")); - assertEquals(3, driver.getRowCountDeleted("person")); - assertEquals(0, driver.getRowCount("person")); - - // One more realistic sample. - if (driver.exists("cliente")) - driver.executeUpdate("drop table cliente"); - driver.execute("create table cliente (CODCLI char( 10 ), CODIGO char( 10 ), NOMEFANTASIA char( 50 ), RAZAOSOCIAL char( 65 ), " - + "CONTATO char( 50 ), ENDERECO char( 55 ), BAIRRO char( 20 ), CIDADE char( 40 ), ESTADO char( 2 ), CEP char( 8 ), TELEFONE char( 55 ), " - + "CNPJCPF char( 18 ), INSCRESTADUALRG char( 20 ), PESSOA char( 1 ), EMAIL char( 50 ), BLOQUEIAVENDA char( 2 ), CODVENDED char( 10 ), " - + "LIMITECREDITO double, OBS char( 200 ), TRANSMITIDO char( 1 ), CODCATEGORIA char( 10 ), TITULOSVENCIDOS double, NUMEROLOGRADOURO char( 10 ), " - + "CODFP char( 10 ), CODTRANS char( 10 ), CODPRECO char( 10 ), COMPLEMENTOLOGRADOURO char( 50 ) )"); - driver.execute("create index idx_rowid on cliente(rowid)"); - driver.execute("create index idx_CODCLI on cliente(CODCLI)"); - driver.execute("create index idx_NOMEFANTASIA on cliente(NOMEFANTASIA)"); - driver.execute("create index idx_RAZAOSOCIAL on cliente(RAZAOSOCIAL)"); - driver.execute("create index idx_CODVENDED on cliente(CODVENDED)"); - driver.executeQuery("select rowid,NOMEFANTASIA,RAZAOSOCIAL,CODIGO,BLOQUEIAVENDA,CNPJCPF,CODCLI from cliente " - + "where RAZAOSOCIAL like 'A%' and CODVENDED = '00000074' order by NOMEFANTASIA").close(); - - // One more realistic sample. - if (driver.exists("cadclientes")) - driver.executeUpdate("drop table cadclientes"); - driver.execute("create table CADCLIENTES (id int, CODCLIPRE char(5), RAZAO char(60), ENDERECO char(40), REFENDER char(25), BAIRRO char(20), " - + "CIDADE char(20), UF char(2), CEP char(8), CNPJ char(14), CPF char(11), INSCEST char(15), INSCMUNIC char(15), RG char(15), ORGEMIS char(10), " - + "TEL char(14), FAX char(14), CONTATO char(15), DATCAD datetime, CODVEND char(3), PROXCLI char(5), EMAIL char(60), CODESTAB char(3), " - + "TIPOCLI char(1), ENV char(1), REENV char(1), FANTASIA char(40), SUBBAIRRO char(20), primary key (id))"); - driver.executeUpdate("insert into CADCLIENTES values (-1, null, 'TESTE', 'ASD', null, 'ASD', 'ASD', 'AL', '22222222', null, '10101142714', " - + "null, null, 'ASD', 'ASD', '123', '', 'ASD', null, '029', null, '', '001', 'F', 'N', 'N', 'TESTE', '')"); - driver.executeUpdate("update CADCLIENTES set id = 37, CODCLIPRE = 'YTPEE', ENV = 'S' where id = -1"); - (resultSet = driver.executeQuery("select id from CADCLIENTES")).first(); - assertEquals(37, resultSet.getInt("id")); - resultSet.close(); - driver.closeAll(); - }; - - /** - * Tests a query. - * - * @param driver The connection with Litebase. - * @param sql The query to be executed - * @return The number of rows returned by the query. - */ - private int executeQuery(LitebaseConnection driver, String sql) - { - int count = 0; - ResultSet resultSet = driver.executeQuery(sql); - while (resultSet.next()) - count++; - resultSet.close(); - return count; - } - - /** - * Tests Litebase queries with and without indices. - * - * @param driver - */ - private void testQueries(LitebaseConnection driver) - { - // Exact key search. - assertEquals(2, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'guilherme'")); - assertEquals(19, executeQuery(driver, "select * from PERSON where FIRST_NAME != 'guilherme' and FIRST_NAME is not null")); - - // Exact search with OR. - assertEquals(5, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'guilherme' or FIRST_NAME = 'roberto'")); - assertEquals(7, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'roberto' or city = 'Porto Seguro'")); - - // Exact search with AND. - assertEquals(1, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and city = 'Paraty'")); - assertEquals(3, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and YEARS_EXP_C = 6")); - - // Exact query with function. - assertEquals(2, executeQuery(driver, "select * from PERSON where upper(FIRST_NAME) = 'GUILHERME'")); - assertEquals(19, executeQuery(driver, "select * from PERSON where upper(FIRST_NAME) != 'GUILHERME'")); - - // Non-exact search with OR. - assertEquals(13, executeQuery(driver, "select * from PERSON where salary_prev < 3000 or years_exp_C = 10 or years_exp_java > 20")); - assertEquals(6, executeQuery(driver, "select * from PERSON where city = 'Ouro Preto' or SALARY_CUR > 10000 or years_exp_C = 12")); - assertEquals(9, executeQuery(driver, "select * from PERSON where city = 'Paraty' or years_exp_Java > 10 or years_exp_C > 18")); - assertEquals(19, executeQuery(driver, "select * from PERSON where (years_exp_C > 10 or years_exp_C < 10) and years_exp_C is not null")); - - // Non-exact search with AND. - assertEquals(2, executeQuery(driver, "select * from PERSON where salary_prev < 3000 and years_exp_C = 10 and years_exp_java > 3")); - assertEquals(1, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' and SALARY_CUR > 3000 and years_exp_C = 6")); - assertEquals(2, executeQuery(driver, "select * from PERSON where city = 'Rio de Janeiro' and years_exp_Java > 10 and years_exp_C > 3")); - assertEquals(2, executeQuery(driver, "select * from PERSON where city = 'Rio de Janeiro' and years_exp_C < 10 and years_exp_C > 3")); - - // Complex queries. - assertEquals(5, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 and years_exp_C = 10) or city = 'Natal'")); - assertEquals(8, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'maria' or (SALARY_CUR > 3000 and years_exp_C = 6) " - + "or city = 'Fortaleza'")); - assertEquals(5, executeQuery(driver, "select * from PERSON where city = 'Rio de Janeiro' and (years_exp_Java > 10 or years_exp_C > 3)")); - assertEquals(2, executeQuery(driver, "select * from PERSON where (salary_prev < 3000 or years_exp_C = 10) and city = 'Porto Seguro' " - + "and years_exp_C > 2")); - - // Null queries. - assertEquals(20, executeQuery(driver, "select * from PERSON where FIRST_NAME != 'null' and FIRST_NAME is not null")); - assertEquals(1, executeQuery(driver, "select * from PERSON where FIRST_NAME = 'null'")); - assertEquals(21, executeQuery(driver, "select * from PERSON where FIRST_NAME is not null")); - assertEquals(1, executeQuery(driver, "select * from PERSON where FIRST_NAME is null")); - } -} diff --git a/LitebaseSDK/src/java/samples/sys/testcases/inserts.txt b/LitebaseSDK/src/java/samples/sys/testcases/inserts.txt deleted file mode 100644 index 36a1d78432..0000000000 --- a/LitebaseSDK/src/java/samples/sys/testcases/inserts.txt +++ /dev/null @@ -1,1338 +0,0 @@ -compras values (7804, '01510441', '00124', '2011/10/24', null, 14, 382380, '029') -compras values (27008, '01525686', '00124', '2011/11/21', null, 14, 761880, '029') -compras values (31682, '01529274', '00124', '2011/11/28', null, 14, 343840, '029') -compras values (12444, '01514767', '00508', '2011/11/02', null, 14, 208510, '029') -compras values (16771, '01517118', '00508', '2011/11/06', null, 14, 100300, '029') -compras values (27010, '01525594', '00508', '2011/11/21', null, 14, 190430, '029') -compras values (27996, '01526256', '00508', '2011/11/22', null, 14, 586360, '029') -compras values (29744, '01527065', '00508', '2011/11/24', null, 14, 209000, '029') -compras values (66, '01505227', '01201', '2011/10/13', null, 21, 1896730, '029') -compras values (65, '01507337', '01201', '2011/10/18', null, 21, 508280, '029') -compras values (64, '01508742', '01201', '2011/10/20', null, 21, 953720, '029') -compras values (12451, '01515269', '01201', '2011/11/02', null, 21, 248000, '029') -compras values (15064, '01516124', '01201', '2011/11/03', null, 21, 673730, '029') -compras values (18308, '01518705', '01201', '2011/11/08', null, 21, 1017060, '029') -compras values (23935, '01523147', '01201', '2011/11/16', null, 21, 628700, '029') -compras values (28917, '01526987', '01201', '2011/11/23', null, 21, 373560, '029') -compras values (67, '01505232', '01219', '2011/10/13', null, 7, 713800, '029') -compras values (15065, '01516129', '01219', '2011/11/03', null, 7, 784360, '029') -compras values (16253, '01516651', '01219', '2011/11/04', null, 7, 7888800, '029') -compras values (23946, '01524079', '01219', '2011/11/17', null, 7, 633600, '029') -compras values (116, '01502820', '02176', '2011/10/10', null, 10, 462400, '029') -compras values (115, '01506292', '02176', '2011/10/17', null, 10, 599520, '029') -compras values (12464, '01513471', '02176', '2011/10/31', null, 10, -800, '029') -compras values (21838, '01521584', '02176', '2011/11/14', null, 10, 811110, '029') -compras values (153, '01502819', '02528', '2011/10/10', null, 10, 392700, '029') -compras values (152, '01506291', '02528', '2011/10/17', null, 10, 562400, '029') -compras values (7823, '01509902', '02528', '2011/10/24', null, 10, 532060, '029') -compras values (17270, '01517481', '02528', '2011/11/07', null, 10, 485760, '029') -compras values (21851, '01521583', '02528', '2011/11/14', null, 10, 381940, '029') -compras values (27017, '01525097', '02528', '2011/11/21', null, 10, 489750, '029') -compras values (31707, '01528785', '02528', '2011/11/28', null, 10, 366810, '029') -compras values (7826, '01511148', '02602', '2011/10/25', null, 10, 398600, '029') -compras values (7827, '01509903', '02604', '2011/10/24', null, 10, 210800, '029') -compras values (12480, '01514597', '02604', '2011/11/01', null, 10, 428830, '029') -compras values (27018, '01525098', '02604', '2011/11/21', null, 10, 316400, '029') -compras values (7828, '01510607', '02609', '2011/10/25', null, 5, 278200, '029') -compras values (12481, '01513470', '02609', '2011/10/31', null, 5, 322340, '029') -compras values (17272, '01517480', '02609', '2011/11/07', null, 5, 318660, '029') -compras values (21853, '01521582', '02609', '2011/11/14', null, 5, 416050, '029') -compras values (27019, '01525096', '02609', '2011/11/21', null, 5, 429170, '029') -compras values (31710, '01528784', '02609', '2011/11/28', null, 5, 534410, '029') -compras values (31709, '01529500', '02609', '2011/11/29', null, 5, 71020, '029') -compras values (162, '01507628', '02611', '2011/10/19', null, 14, 152460, '029') -compras values (7829, '01509904', '02611', '2011/10/24', null, 14, 216080, '029') -compras values (15085, '01515546', '02611', '2011/11/03', null, 14, 129860, '029') -compras values (17273, '01517479', '02611', '2011/11/07', null, 14, 190380, '029') -compras values (27020, '01525099', '02611', '2011/11/21', null, 14, 189470, '029') -compras values (31711, '01528786', '02611', '2011/11/28', null, 14, 224600, '029') -compras values (269, '01505758', '03884', '2011/10/14', null, 14, 147250, '029') -compras values (268, '01508842', '03884', '2011/10/20', null, 14, 557000, '029') -compras values (12501, '01515264', '03884', '2011/11/02', null, 14, 405000, '029') -compras values (21397, '01520810', '03884', '2011/11/11', null, 14, 606770, '029') -compras values (24006, '01524015', '03884', '2011/11/17', null, 14, 628700, '029') -compras values (29787, '01527783', '03884', '2011/11/24', null, 14, 649460, '029') -compras values (279, '01503263', '04195', '2011/10/10', null, 10, 627160, '029') -compras values (15104, '01516115', '04195', '2011/11/03', null, 10, 562030, '029') -compras values (21877, '01521735', '04195', '2011/11/14', null, 10, 248070, '029') -compras values (24008, '01523137', '04195', '2011/11/16', null, 10, 679150, '029') -compras values (28933, '01526981', '04195', '2011/11/23', null, 10, 220480, '029') -compras values (30828, '01528155', '04195', '2011/11/25', null, 10, 226620, '029') -compras values (313, '01505389', '04750', '2011/10/13', null, 14, 1016020, '029') -compras values (312, '01507436', '04750', '2011/10/18', null, 13, 1060290, '029') -compras values (311, '01507437', '04750', '2011/10/18', null, 14, 832150, '029') -compras values (7860, '01510420', '04750', '2011/10/24', null, 13, 900270, '029') -compras values (7859, '01510421', '04750', '2011/10/24', null, 14, 939610, '029') -compras values (9757, '01511765', '04750', '2011/10/26', null, 13, 960800, '029') -compras values (9756, '01511766', '04750', '2011/10/26', null, 14, 1024030, '029') -compras values (15113, '01516278', '04750', '2011/11/03', null, 13, 798300, '029') -compras values (15112, '01516279', '04750', '2011/11/03', null, 14, 809600, '029') -compras values (16282, '01516755', '04750', '2011/11/04', null, 14, 1028700, '029') -compras values (20278, '01520426', '04750', '2011/11/10', null, 11, 727500, '029') -compras values (20277, '01520427', '04750', '2011/11/10', null, 13, 922430, '029') -compras values (20276, '01520428', '04750', '2011/11/10', null, 14, 846300, '029') -compras values (24018, '01524021', '04750', '2011/11/17', null, 14, 960870, '029') -compras values (24017, '01524022', '04750', '2011/11/17', null, 13, 978750, '029') -compras values (31745, '01529976', '04750', '2011/11/29', null, 13, 964040, '029') -compras values (31744, '01529977', '04750', '2011/11/29', null, 14, 794770, '029') -compras values (10665, '01512467', '07017', '2011/10/27', null, 14, 370360, '029') -compras values (15139, '01516134', '07017', '2011/11/03', null, 14, 363720, '029') -compras values (24076, '01523161', '07017', '2011/11/16', null, 14, 573620, '029') -compras values (10666, '01511970', '07045', '2011/10/27', null, 7, 343400, '029') -compras values (28965, '01526626', '07508', '2011/11/23', null, 10, 209780, '029') -compras values (550, '01503686', '07638', '2011/10/11', null, 21, 449280, '029') -compras values (18372, '01518400', '07638', '2011/11/08', null, 21, 181800, '029') -compras values (20309, '01519892', '07638', '2011/11/10', null, 21, 163350, '029') -compras values (21944, '01521658', '07638', '2011/11/14', null, 21, 283080, '029') -compras values (24092, '01523559', '07638', '2011/11/17', null, 21, 603720, '029') -compras values (27068, '01525258', '07638', '2011/11/21', null, 21, 529620, '029') -compras values (31807, '01528866', '07638', '2011/11/28', null, 21, 321990, '029') -compras values (31806, '01529619', '07638', '2011/11/29', null, 21, 149370, '029') -compras values (572, '01502818', '07799', '2011/10/10', null, 10, 111360, '029') -compras values (571, '01506290', '07799', '2011/10/17', null, 10, 123520, '029') -compras values (7941, '01509901', '07799', '2011/10/24', null, 10, 120960, '029') -compras values (12596, '01513469', '07799', '2011/10/31', null, 10, 121290, '029') -compras values (21952, '01521581', '07799', '2011/11/14', null, 10, 123420, '029') -compras values (27070, '01525095', '07799', '2011/11/21', null, 10, 128040, '029') -compras values (31813, '01528783', '07799', '2011/11/28', null, 10, 147400, '029') -compras values (590, '01503737', '07920', '2011/10/11', null, 21, 879590, '029') -compras values (589, '01506046', '07920', '2011/10/16', null, 15, 167310, '029') -compras values (16297, '01516706', '07920', '2011/11/04', null, 21, 715020, '029') -compras values (21956, '01521762', '07920', '2011/11/14', null, 21, 864650, '029') -compras values (27074, '01525633', '07920', '2011/11/21', null, 15, 126020, '029') -compras values (12602, '02514311', '07920', '2011/10/31', null, 21, 810960, '029') -compras values (593, '01502731', '07921', '2011/10/10', null, 21, 329030, '029') -compras values (592, '01504706', '07921', '2011/10/13', null, 16, 160440, '029') -compras values (591, '01506271', '07921', '2011/10/17', null, 21, 296070, '029') -compras values (7946, '01509881', '07921', '2011/10/24', null, 21, 226070, '029') -compras values (7945, '01510625', '07921', '2011/10/25', null, 21, 301600, '029') -compras values (10688, '01511953', '07921', '2011/10/27', null, 21, 644360, '029') -compras values (12604, '01513363', '07921', '2011/10/31', null, 21, 291880, '029') -compras values (12603, '01514570', '07921', '2011/11/01', null, 21, 330000, '029') -compras values (15153, '01515525', '07921', '2011/11/03', null, 21, 197940, '029') -compras values (17325, '01517435', '07921', '2011/11/07', null, 21, 315410, '029') -compras values (20313, '01519750', '07921', '2011/11/10', null, 21, 323710, '029') -compras values (21411, '01520581', '07921', '2011/11/11', null, 21, 371470, '029') -compras values (21957, '01521570', '07921', '2011/11/14', null, 21, 210880, '029') -compras values (27075, '01525128', '07921', '2011/11/21', null, 21, 611640, '029') -compras values (31820, '01528777', '07921', '2011/11/28', null, 21, 413920, '029') -compras values (754, '05244472', '09732', '2011/10/12', null, 10, 81600, '029') -compras values (753, '05245440', '09732', '2011/10/18', null, 10, 119890, '029') -compras values (7980, '05246738', '09732', '2011/10/25', null, 10, 81600, '029') -compras values (15185, '05248495', '09732', '2011/11/03', null, 10, 123110, '029') -compras values (19399, '05249600', '09732', '2011/11/09', null, 10, 83300, '029') -compras values (24139, '05250894', '09732', '2011/11/16', null, 10, 88200, '029') -compras values (28082, '05251981', '09732', '2011/11/22', null, 10, 162650, '029') -compras values (766, '01502733', '09836', '2011/10/10', null, 10, 370000, '029') -compras values (765, '01506881', '09836', '2011/10/18', null, 10, 207660, '029') -compras values (7982, '01509880', '09836', '2011/10/24', null, 10, 553490, '029') -compras values (18398, '01518259', '09836', '2011/11/08', null, 10, 967190, '029') -compras values (27094, '01525129', '09836', '2011/11/21', null, 10, 463650, '029') -compras values (30893, '01528260', '09836', '2011/11/27', null, 10, 348210, '029') -compras values (31857, '01528776', '09836', '2011/11/28', null, 10, 699840, '029') -compras values (867, '01504632', '10382', '2011/10/13', null, 21, 123420, '029') -compras values (866, '01506179', '10382', '2011/10/17', null, 21, 1944500, '029') -compras values (865, '01508103', '10382', '2011/10/20', null, 21, 317200, '029') -compras values (8004, '01510545', '10382', '2011/10/25', null, 21, 354450, '029') -compras values (10765, '01511889', '10382', '2011/10/27', null, 21, 490550, '029') -compras values (12711, '01513276', '10382', '2011/10/31', null, 21, 392390, '029') -compras values (16322, '01516440', '10382', '2011/11/04', null, 21, 850680, '029') -compras values (18411, '01518195', '10382', '2011/11/08', null, 21, 773800, '029') -compras values (20356, '01519688', '10382', '2011/11/10', null, 21, 81600, '029') -compras values (24169, '01522611', '10382', '2011/11/16', null, 21, 789530, '029') -compras values (28094, '01525825', '10382', '2011/11/22', null, 21, 497000, '029') -compras values (31882, '01528721', '10382', '2011/11/28', null, 21, 273750, '029') -compras values (31881, '01529396', '10382', '2011/11/29', null, 21, 331500, '029') -compras values (942, '01509302', '10737', '2011/10/23', null, 10, 200060, '029') -compras values (17373, '01517455', '11164', '2011/11/07', null, 21, 9890160, '029') -compras values (1276, '01506248', '13087', '2011/10/17', null, 21, 755200, '029') -compras values (1275, '01509386', '13087', '2011/10/23', null, 21, 394500, '029') -compras values (8115, '01509853', '13087', '2011/10/24', null, 21, 287240, '029') -compras values (9854, '01511315', '13087', '2011/10/26', null, 21, 401450, '029') -compras values (10861, '01512975', '13087', '2011/10/30', null, 21, 617760, '029') -compras values (12855, '01513443', '13087', '2011/10/31', null, 21, 703380, '029') -compras values (17424, '01517420', '13087', '2011/11/07', null, 21, 565940, '029') -compras values (22136, '01520919', '13087', '2011/11/13', null, 21, 590040, '029') -compras values (22135, '01521508', '13087', '2011/11/14', null, 21, 551590, '029') -compras values (27170, '01525053', '13087', '2011/11/21', null, 21, 519880, '029') -compras values (31990, '01528703', '13087', '2011/11/28', null, 21, 536580, '029') -compras values (1434, '05243990', '14100', '2011/10/10', null, 14, 321530, '029') -compras values (1433, '05245438', '14100', '2011/10/18', null, 14, 399080, '029') -compras values (12917, '05248263', '14100', '2011/11/02', null, 14, 225900, '029') -compras values (17455, '05249082', '14100', '2011/11/07', null, 10, 476920, '029') -compras values (22190, '05250675', '14100', '2011/11/15', null, 14, 351900, '029') -compras values (27195, '05251763', '14100', '2011/11/21', null, 14, 419260, '029') -compras values (1591, '01502674', '14770', '2011/10/10', null, 21, 164920, '029') -compras values (1590, '01504623', '14770', '2011/10/13', null, 21, 583470, '029') -compras values (1589, '01508092', '14770', '2011/10/20', null, 21, 369870, '029') -compras values (10930, '01511881', '14770', '2011/10/27', null, 21, 364140, '029') -compras values (18511, '01518185', '14770', '2011/11/08', null, 21, 171340, '029') -compras values (20426, '01519673', '14770', '2011/11/10', null, 21, 271520, '029') -compras values (22230, '01521545', '14770', '2011/11/14', null, 21, 250770, '029') -compras values (27215, '01525014', '14770', '2011/11/21', null, 21, 262440, '029') -compras values (29947, '01527126', '14770', '2011/11/24', null, 21, 140640, '029') -compras values (32067, '01528715', '14770', '2011/11/28', null, 21, 391480, '029') -compras values (24365, '01523308', '14778', '2011/11/17', null, 21, 368290, '029') -compras values (1593, '01503421', '14781', '2011/10/10', null, 21, 1206780, '029') -compras values (1592, '01507429', '14781', '2011/10/18', null, 21, 1232000, '029') -compras values (8204, '01511142', '14781', '2011/10/25', null, 21, 611700, '029') -compras values (19505, '01519552', '14781', '2011/11/09', null, 21, 959650, '029') -compras values (24366, '01523205', '14781', '2011/11/16', null, 21, 387900, '029') -compras values (12978, '02514375', '14781', '2011/10/31', null, 21, 468840, '029') -compras values (12977, '02514387', '14781', '2011/10/31', null, 21, 365340, '029') -compras values (1609, '01502554', '14838', '2011/10/10', null, 21, 93500, '029') -compras values (1608, '01504576', '14838', '2011/10/13', null, 21, 592470, '029') -compras values (1607, '01505802', '14838', '2011/10/16', null, 21, 806130, '029') -compras values (8206, '01510514', '14838', '2011/10/25', null, 15, 159000, '029') -compras values (12982, '01514781', '14838', '2011/11/02', null, 21, 330740, '029') -compras values (15295, '01515678', '14838', '2011/11/03', null, 21, 656080, '029') -compras values (15294, '01515679', '14838', '2011/11/03', null, 21, 587930, '029') -compras values (17481, '01517253', '14838', '2011/11/07', null, 21, 143590, '029') -compras values (18513, '01518137', '14838', '2011/11/08', null, 21, 712660, '029') -compras values (20431, '01519651', '14838', '2011/11/10', null, 21, 331270, '029') -compras values (20430, '01519652', '14838', '2011/11/10', null, 21, 646250, '029') -compras values (22236, '01520874', '14838', '2011/11/13', null, 21, 188430, '029') -compras values (24371, '01523266', '14838', '2011/11/17', null, 21, 622440, '029') -compras values (27216, '01524961', '14838', '2011/11/21', null, 21, 297080, '029') -compras values (28196, '01525758', '14838', '2011/11/22', null, 21, 217340, '029') -compras values (31018, '01528286', '14838', '2011/11/27', null, 21, 179600, '029') -compras values (32070, '01528608', '14838', '2011/11/28', null, 14, 122640, '029') -compras values (1619, '01507985', '14905', '2011/10/19', null, 21, 940380, '029') -compras values (1618, '01507986', '14905', '2011/10/19', null, 21, 99000, '029') -compras values (1617, '01509511', '14905', '2011/10/23', null, 21, 418500, '029') -compras values (8214, '01510381', '14905', '2011/10/24', null, 21, 137000, '029') -compras values (10935, '01513227', '14905', '2011/10/30', null, 21, 98000, '029') -compras values (12987, '01515325', '14905', '2011/11/02', null, 21, 1706240, '029') -compras values (12986, '01515340', '14905', '2011/11/02', null, 21, 1594080, '029') -compras values (15297, '01516194', '14905', '2011/11/03', null, 21, 109760, '029') -compras values (15296, '01516214', '14905', '2011/11/03', null, 21, 189000, '029') -compras values (16883, '01517166', '14905', '2011/11/06', null, 21, 147000, '029') -compras values (19509, '01519449', '14905', '2011/11/09', null, 21, 147000, '029') -compras values (19508, '01519513', '14905', '2011/11/09', null, 21, 1347000, '029') -compras values (22239, '01522443', '14905', '2011/11/15', null, 21, 82140, '029') -compras values (24375, '01523136', '14905', '2011/11/16', null, 21, 1725300, '029') -compras values (24374, '01523997', '14905', '2011/11/17', null, 21, 1005120, '029') -compras values (24373, '01524085', '14905', '2011/11/17', null, 21, 147000, '029') -compras values (29084, '01526952', '14905', '2011/11/23', null, 21, 1615500, '029') -compras values (31020, '01528496', '14905', '2011/11/27', null, 21, 397900, '029') -compras values (32074, '01529951', '14905', '2011/11/29', null, 21, 56300, '029') -compras values (32073, '01529952', '14905', '2011/11/29', null, 21, 170000, '029') -compras values (1652, '01502040', '15127', '2011/10/09', null, 21, 375290, '029') -compras values (10943, '01511919', '15127', '2011/10/27', null, 15, 169320, '029') -compras values (29087, '01526591', '15127', '2011/11/23', null, 21, 965140, '029') -compras values (8261, '01509884', '15733', '2011/10/24', null, 21, 725950, '029') -compras values (29966, '01527164', '15733', '2011/11/24', null, 21, 984330, '029') -compras values (1860, '01505909', '16111', '2011/10/16', null, 21, 1620810, '029') -compras values (1859, '01508200', '16111', '2011/10/20', null, 15, 138360, '029') -compras values (1858, '01509288', '16111', '2011/10/23', null, 21, 165060, '029') -compras values (10979, '01512047', '16111', '2011/10/27', null, 15, 221240, '029') -compras values (10978, '01513017', '16111', '2011/10/30', null, 21, 1367410, '029') -compras values (13088, '01513707', '16111', '2011/10/31', null, 21, 276520, '029') -compras values (13086, '01514909', '16111', '2011/11/02', null, 21, 186490, '029') -compras values (15333, '01515575', '16111', '2011/11/03', null, 21, 229280, '029') -compras values (16898, '01516849', '16111', '2011/11/06', null, 21, 260570, '029') -compras values (20461, '01519776', '16111', '2011/11/10', null, 21, 268580, '029') -compras values (22294, '01520987', '16111', '2011/11/13', null, 21, 373430, '029') -compras values (24454, '01523518', '16111', '2011/11/17', null, 21, 290530, '029') -compras values (26316, '01524666', '16111', '2011/11/20', null, 21, 218410, '029') -compras values (29975, '01527206', '16111', '2011/11/24', null, 21, 307330, '029') -compras values (31040, '01528343', '16111', '2011/11/27', null, 21, 235690, '029') -compras values (13087, '02514279', '16111', '2011/10/31', null, 21, 276520, '029') -compras values (1866, '01505892', '16137', '2011/10/16', null, 14, 710360, '029') -compras values (1865, '01507609', '16137', '2011/10/19', null, 14, 224700, '029') -compras values (1864, '01508174', '16137', '2011/10/20', null, 14, 582270, '029') -compras values (10980, '01511954', '16137', '2011/10/27', null, 14, 738440, '029') -compras values (13090, '01513365', '16137', '2011/10/31', null, 14, 274650, '029') -compras values (15334, '01515527', '16137', '2011/11/03', null, 14, 504290, '029') -compras values (16397, '01516547', '16137', '2011/11/04', null, 14, 163450, '029') -compras values (20462, '01519751', '16137', '2011/11/10', null, 14, 1211770, '029') -compras values (22295, '01520936', '16137', '2011/11/13', null, 14, 247500, '029') -compras values (24456, '01523420', '16137', '2011/11/17', null, 14, 896400, '029') -compras values (26319, '01524277', '16137', '2011/11/18', null, 14, 326960, '029') -compras values (26318, '01524629', '16137', '2011/11/20', null, 14, 365200, '029') -compras values (28225, '01525886', '16137', '2011/11/22', null, 14, 561220, '029') -compras values (29976, '01527161', '16137', '2011/11/24', null, 14, 1427140, '029') -compras values (31041, '01528259', '16137', '2011/11/27', null, 14, 242460, '029') -compras values (1885, '05244715', '16181', '2011/10/13', null, 7, 324160, '029') -compras values (1884, '05245441', '16181', '2011/10/18', null, 7, 250860, '029') -compras values (8283, '05246739', '16181', '2011/10/25', null, 7, 308260, '029') -compras values (13096, '05248103', '16181', '2011/11/01', null, 7, 257270, '029') -compras values (18544, '05249329', '16181', '2011/11/08', null, 7, 265610, '029') -compras values (24460, '05250893', '16181', '2011/11/16', null, 7, 289250, '029') -compras values (28228, '05251982', '16181', '2011/11/22', null, 7, 256640, '029') -compras values (32137, '05253239', '16181', '2011/11/29', null, 7, 272210, '029') -compras values (2319, '01502112', '18359', '2011/10/09', null, 10, 576900, '029') -compras values (2318, '01502739', '18359', '2011/10/10', null, 10, 476600, '029') -compras values (22446, '01521641', '18506', '2011/11/14', null, 21, 279240, '029') -compras values (24576, '01523567', '18506', '2011/11/17', null, 21, 318400, '029') -compras values (26380, '01524362', '18506', '2011/11/18', null, 21, 551820, '029') -compras values (2381, '01509113', '18744', '2011/10/21', null, 21, 431900, '029') -compras values (22454, '01521221', '18744', '2011/11/13', null, 15, 404250, '029') -compras values (32265, '01529178', '18744', '2011/11/28', null, 21, 1270930, '029') -compras values (2422, '01507421', '18935', '2011/10/18', null, 14, 1959420, '029') -compras values (17597, '01517976', '18935', '2011/11/07', null, 14, 609980, '029') -compras values (20545, '01520401', '18935', '2011/11/10', null, 14, 601570, '029') -compras values (2532, '01505390', '19297', '2011/10/13', null, 14, 138710, '029') -compras values (8429, '01510422', '19297', '2011/10/24', null, 14, 131840, '029') -compras values (15429, '01516280', '19297', '2011/11/03', null, 14, 176670, '029') -compras values (24622, '01524023', '19297', '2011/11/17', null, 14, 200530, '029') -compras values (32294, '01529978', '19297', '2011/11/29', null, 14, 176320, '029') -compras values (2660, '01502732', '19913', '2011/10/10', null, 15, 398370, '029') -compras values (2659, '01506272', '19913', '2011/10/17', null, 15, 318120, '029') -compras values (2658, '01508920', '19913', '2011/10/21', null, 15, 231970, '029') -compras values (8470, '01509879', '19913', '2011/10/24', null, 15, 214250, '029') -compras values (11185, '01511955', '19913', '2011/10/27', null, 15, 150950, '029') -compras values (13369, '01513364', '19913', '2011/10/31', null, 15, 466970, '029') -compras values (15448, '01515526', '19913', '2011/11/03', null, 15, 330820, '029') -compras values (19625, '01519017', '19913', '2011/11/09', null, 15, 426760, '029') -compras values (22517, '01521571', '19913', '2011/11/14', null, 15, 524970, '029') -compras values (27352, '01525130', '19913', '2011/11/21', null, 15, 399950, '029') -compras values (2684, '01502741', '19998', '2011/10/10', null, 15, 465220, '029') -compras values (2683, '01506287', '19998', '2011/10/17', null, 15, 453670, '029') -compras values (2682, '01508922', '19998', '2011/10/21', null, 15, 202370, '029') -compras values (8475, '01509892', '19998', '2011/10/24', null, 15, 263360, '029') -compras values (11195, '01511965', '19998', '2011/10/27', null, 15, 220240, '029') -compras values (13376, '01513382', '19998', '2011/10/31', null, 15, 355860, '029') -compras values (15453, '01515537', '19998', '2011/11/03', null, 15, 314940, '029') -compras values (17632, '01517457', '19998', '2011/11/07', null, 15, 306290, '029') -compras values (20585, '01519767', '19998', '2011/11/10', null, 15, 344000, '029') -compras values (22526, '01521579', '19998', '2011/11/14', null, 15, 430530, '029') -compras values (27356, '01525142', '19998', '2011/11/21', null, 15, 316420, '029') -compras values (30084, '01527174', '19998', '2011/11/24', null, 15, 155250, '029') -compras values (32325, '01529483', '19998', '2011/11/29', null, 15, 302020, '029') -compras values (2890, '01504716', '20612', '2011/10/13', null, 21, 461040, '029') -compras values (2889, '01506286', '20612', '2011/10/17', null, 21, 907400, '029') -compras values (2888, '01508925', '20612', '2011/10/21', null, 21, 390000, '029') -compras values (8528, '01510634', '20612', '2011/10/25', null, 21, 719600, '029') -compras values (10031, '01511373', '20612', '2011/10/26', null, 21, 77000, '029') -compras values (13438, '01513379', '20612', '2011/10/31', null, 21, 1514290, '029') -compras values (18669, '01518266', '20612', '2011/11/08', null, 21, 269200, '029') -compras values (24699, '01522699', '20612', '2011/11/16', null, 21, 956840, '029') -compras values (28348, '01525891', '20612', '2011/11/22', null, 21, 1324050, '029') -compras values (32363, '01528775', '20612', '2011/11/28', null, 21, 354020, '029') -compras values (2893, '01506163', '20614', '2011/10/17', null, 21, 2954970, '029') -compras values (2892, '01506164', '20614', '2011/10/17', null, 20, 622200, '029') -compras values (2891, '01508159', '20614', '2011/10/20', null, 21, 764400, '029') -compras values (8529, '01510603', '20614', '2011/10/25', null, 21, 2181700, '029') -compras values (10032, '01511238', '20614', '2011/10/26', null, 21, 1054000, '029') -compras values (13439, '01513337', '20614', '2011/10/31', null, 21, 2246040, '029') -compras values (15477, '01515471', '20614', '2011/11/03', null, 21, 571200, '029') -compras values (16478, '01516479', '20614', '2011/11/04', null, 21, 816000, '029') -compras values (18670, '01518183', '20614', '2011/11/08', null, 21, 644400, '029') -compras values (21604, '01520533', '20614', '2011/11/11', null, 21, 1239700, '029') -compras values (24701, '01522596', '20614', '2011/11/16', null, 21, 1602000, '029') -compras values (24700, '01523386', '20614', '2011/11/17', null, 21, 915960, '029') -compras values (26452, '01524164', '20614', '2011/11/18', null, 21, 0, '029') -compras values (28349, '01525850', '20614', '2011/11/22', null, 21, 5227380, '029') -compras values (32364, '01528682', '20614', '2011/11/28', null, 21, 2420300, '029') -compras values (2955, '01505356', '21049', '2011/10/13', null, 7, 403150, '029') -compras values (13461, '01514398', '21049', '2011/10/31', null, 7, 423080, '029') -compras values (16484, '01516798', '21049', '2011/11/04', null, 7, 455920, '029') -compras values (26462, '01524501', '21049', '2011/11/18', null, 7, 461340, '029') -compras values (32383, '01529272', '21049', '2011/11/28', null, 7, 404620, '029') -compras values (2968, '01503264', '21098', '2011/10/10', null, 14, 257950, '029') -compras values (2967, '01507329', '21098', '2011/10/18', null, 14, 540830, '029') -compras values (8546, '01510981', '21098', '2011/10/25', null, 14, 272550, '029') -compras values (15485, '01516116', '21098', '2011/11/03', null, 14, 3193470, '029') -compras values (16487, '01516647', '21098', '2011/11/04', null, 14, 259250, '029') -compras values (24718, '01523138', '21098', '2011/11/16', null, 14, 310980, '029') -compras values (13464, '02514228', '21098', '2011/10/31', null, 14, 190000, '029') -compras values (3247, '01506626', '22443', '2011/10/17', null, 21, 515150, '029') -compras values (3246, '01509487', '22443', '2011/10/23', null, 21, 683700, '029') -compras values (11328, '01512462', '22443', '2011/10/27', null, 21, 2980610, '029') -compras values (11327, '01512463', '22443', '2011/10/27', null, 21, 538170, '029') -compras values (15533, '01516123', '22443', '2011/11/03', null, 21, 407700, '029') -compras values (21630, '01520746', '22443', '2011/11/11', null, 21, 760420, '029') -compras values (26507, '01524783', '22443', '2011/11/20', null, 21, 571490, '029') -compras values (26506, '01524784', '22443', '2011/11/20', null, 21, 669740, '029') -compras values (30159, '01527704', '22443', '2011/11/24', null, 21, 934980, '029') -compras values (32455, '01529182', '22443', '2011/11/28', null, 21, 436330, '029') -compras values (32454, '01529891', '22443', '2011/11/29', null, 21, 496400, '029') -compras values (3325, '01502737', '22820', '2011/10/10', null, 15, 411630, '029') -compras values (3324, '01506886', '22820', '2011/10/18', null, 15, 348050, '029') -compras values (8627, '01509893', '22820', '2011/10/24', null, 15, 483370, '029') -compras values (13574, '01513378', '22820', '2011/10/31', null, 15, 303480, '029') -compras values (17715, '01517451', '22820', '2011/11/07', null, 15, 157950, '029') -compras values (19691, '01519022', '22820', '2011/11/09', null, 15, 187440, '029') -compras values (24811, '01523426', '22820', '2011/11/17', null, 15, 275190, '029') -compras values (3445, '05244471', '23624', '2011/10/12', null, 10, 162630, '029') -compras values (18747, '05249328', '23624', '2011/11/08', null, 10, 250870, '029') -compras values (3546, '01505924', '24105', '2011/10/16', null, 21, 326250, '029') -compras values (8683, '01510058', '24105', '2011/10/24', null, 21, 230350, '029') -compras values (22757, '01521657', '24105', '2011/11/14', null, 21, 316200, '029') -compras values (26538, '01524355', '24105', '2011/11/18', null, 21, 306550, '029') -compras values (27473, '01525256', '24105', '2011/11/21', null, 21, 187950, '029') -compras values (29299, '01526655', '24105', '2011/11/23', null, 21, 131340, '029') -compras values (3683, '01503262', '24726', '2011/10/10', null, 14, 186530, '029') -compras values (3682, '01506619', '24726', '2011/10/17', null, 14, 56700, '029') -compras values (13687, '01514685', '24726', '2011/11/01', null, 14, 255880, '029') -compras values (20734, '01520326', '24726', '2011/11/10', null, 14, 189230, '029') -compras values (24915, '01524069', '24726', '2011/11/17', null, 14, 149480, '029') -compras values (28443, '01526257', '24726', '2011/11/22', null, 14, 757420, '029') -compras values (29312, '01526980', '24726', '2011/11/23', null, 14, 74240, '029') -compras values (32551, '01529885', '24726', '2011/11/29', null, 14, 238000, '029') -compras values (13688, '02514227', '24726', '2011/10/31', null, 14, 240400, '029') -compras values (3834, '01506317', '25320', '2011/10/17', null, 20, 1980000, '029') -compras values (3833, '01506318', '25320', '2011/10/17', null, 21, 2524760, '029') -compras values (3832, '01507594', '25320', '2011/10/19', null, 21, 5000300, '029') -compras values (8753, '01510689', '25320', '2011/10/25', null, 21, 4365150, '029') -compras values (10124, '01511392', '25320', '2011/10/26', null, 21, 696000, '029') -compras values (10123, '01511393', '25320', '2011/10/26', null, 21, 4518310, '029') -compras values (15626, '01515577', '25320', '2011/11/03', null, 21, 1611840, '029') -compras values (16538, '01516587', '25320', '2011/11/04', null, 21, 308760, '029') -compras values (18800, '01518279', '25320', '2011/11/08', null, 19, 1683720, '029') -compras values (18799, '01518280', '25320', '2011/11/08', null, 20, 2299500, '029') -compras values (18798, '01518281', '25320', '2011/11/08', null, 21, 2453500, '029') -compras values (24955, '01522727', '25320', '2011/11/16', null, 21, 3204230, '029') -compras values (24954, '01523519', '25320', '2011/11/17', null, 21, 450000, '029') -compras values (28461, '01525971', '25320', '2011/11/22', null, 18, 4286950, '029') -compras values (28460, '01525982', '25320', '2011/11/22', null, 21, 3419560, '029') -compras values (31281, '01527953', '25320', '2011/11/25', null, 21, 888000, '029') -compras values (32581, '01528806', '25320', '2011/11/28', null, 18, 2196600, '029') -compras values (32580, '01528807', '25320', '2011/11/28', null, 21, 2120750, '029') -compras values (13723, '02514255', '25320', '2011/10/31', null, 21, 3882040, '029') -compras values (13722, '02514256', '25320', '2011/10/31', null, 21, 1651400, '029') -compras values (13721, '02514257', '25320', '2011/10/31', null, 21, 2543400, '029') -compras values (3919, '01506282', '25625', '2011/10/17', null, 14, 458060, '029') -compras values (8776, '01509894', '25625', '2011/10/24', null, 14, 263360, '029') -compras values (13746, '01513377', '25625', '2011/10/31', null, 14, 385340, '029') -compras values (17787, '01517449', '25625', '2011/11/07', null, 14, 561740, '029') -compras values (22847, '01521577', '25625', '2011/11/14', null, 14, 216040, '029') -compras values (27513, '01525141', '25625', '2011/11/21', null, 14, 214500, '029') -compras values (32596, '01529479', '25625', '2011/11/29', null, 14, 222040, '029') -compras values (4029, '01504019', '26071', '2011/10/12', null, 10, 324110, '029') -compras values (4028, '01504736', '26071', '2011/10/13', null, 10, 462630, '029') -compras values (4027, '01508145', '26071', '2011/10/20', null, 10, 563550, '029') -compras values (11521, '01511937', '26071', '2011/10/27', null, 10, 368950, '029') -compras values (11520, '01512992', '26071', '2011/10/30', null, 10, 107150, '029') -compras values (15660, '01515456', '26071', '2011/11/03', null, 10, 479700, '029') -compras values (20787, '01519736', '26071', '2011/11/10', null, 10, 233330, '029') -compras values (25016, '01523331', '26071', '2011/11/17', null, 10, 537850, '029') -compras values (28479, '01525735', '26071', '2011/11/22', null, 10, 113400, '029') -compras values (30270, '01527260', '26071', '2011/11/24', null, 10, 512800, '029') -compras values (17871, '01517447', '28239', '2011/11/07', null, 21, 464460, '029') -compras values (30341, '01527175', '28239', '2011/11/24', null, 21, 405570, '029') -compras values (32730, '01528774', '28239', '2011/11/28', null, 21, 951580, '029') -compras values (32746, '01528754', '28431', '2011/11/28', null, 14, 377630, '029') -compras values (31367, '01528255', '28745', '2011/11/27', null, 21, 946560, '029') -compras values (4682, '01503435', '28798', '2011/10/10', null, 12, 563200, '029') -compras values (4681, '01508013', '28798', '2011/10/19', null, 13, 649800, '029') -compras values (4680, '01508014', '28798', '2011/10/19', null, 14, 727050, '029') -compras values (27620, '01525658', '28798', '2011/11/21', null, 13, 953310, '029') -compras values (27619, '01525659', '28798', '2011/11/21', null, 14, 912030, '029') -compras values (30369, '01527788', '28798', '2011/11/24', null, 13, 733360, '029') -compras values (30368, '01527789', '28798', '2011/11/24', null, 14, 957600, '029') -compras values (32767, '01529240', '28798', '2011/11/28', null, 14, 1012270, '029') -compras values (4717, '05244637', '28904', '2011/10/12', null, 15, 309630, '029') -compras values (4716, '05245114', '28904', '2011/10/14', null, 15, 718030, '029') -compras values (4715, '05246162', '28904', '2011/10/20', null, 15, 663510, '029') -compras values (8967, '05246653', '28904', '2011/10/24', null, 15, 670740, '029') -compras values (10217, '05247174', '28904', '2011/10/26', null, 15, 823580, '029') -compras values (13999, '05248002', '28904', '2011/10/31', null, 15, 724450, '029') -compras values (18914, '05249497', '28904', '2011/11/08', null, 15, 647070, '029') -compras values (21739, '05250272', '28904', '2011/11/11', null, 15, 802390, '029') -compras values (25207, '05251092', '28904', '2011/11/16', null, 15, 629890, '029') -compras values (29408, '05252456', '28904', '2011/11/23', null, 15, 934810, '029') -compras values (32779, '05253402', '28904', '2011/11/29', null, 15, 978150, '029') -compras values (4779, '01509510', '29116', '2011/10/23', null, 10, 205650, '029') -compras values (4847, '01508178', '29237', '2011/10/20', null, 10, 190660, '029') -compras values (14056, '01513371', '29237', '2011/10/31', null, 10, 263370, '029') -compras values (20912, '01519758', '29237', '2011/11/10', null, 10, 229270, '029') -compras values (30394, '01527170', '29237', '2011/11/24', null, 10, 234910, '029') -compras values (4926, '01505750', '29515', '2011/10/14', null, 21, 680000, '029') -compras values (4925, '01508828', '29515', '2011/10/20', null, 21, 832800, '029') -compras values (14083, '01514660', '29515', '2011/11/01', null, 21, 1202580, '029') -compras values (17936, '01517983', '29515', '2011/11/07', null, 21, 396460, '029') -compras values (20925, '01520409', '29515', '2011/11/10', null, 21, 607990, '029') -compras values (23126, '01521788', '29515', '2011/11/14', null, 21, 1388210, '029') -compras values (26723, '01524470', '29515', '2011/11/18', null, 21, 549000, '029') -compras values (27664, '01525648', '29515', '2011/11/21', null, 21, 1314920, '029') -compras values (32840, '01529225', '29515', '2011/11/28', null, 21, 870350, '029') -compras values (14084, '02514373', '29515', '2011/10/31', null, 21, 1161000, '029') -compras values (4948, '01504700', '29607', '2011/10/13', null, 21, 521810, '029') -compras values (4947, '01506876', '29607', '2011/10/18', null, 21, 165000, '029') -compras values (4946, '01508172', '29607', '2011/10/20', null, 21, 283430, '029') -compras values (14093, '01513361', '29607', '2011/10/31', null, 21, 481040, '029') -compras values (15818, '01515521', '29607', '2011/11/03', null, 21, 445990, '029') -compras values (17098, '01516930', '29607', '2011/11/06', null, 21, 184240, '029') -compras values (19866, '01519015', '29607', '2011/11/09', null, 21, 460650, '029') -compras values (25279, '01522683', '29607', '2011/11/16', null, 21, 169490, '029') -compras values (27669, '01525126', '29607', '2011/11/21', null, 21, 275400, '029') -compras values (32846, '01529474', '29607', '2011/11/29', null, 21, 329130, '029') -compras values (32845, '01529475', '29607', '2011/11/29', null, 21, 424160, '029') -compras values (11782, '01511962', '29878', '2011/10/27', null, 21, 171640, '029') -compras values (17956, '01517443', '29878', '2011/11/07', null, 21, 433550, '029') -compras values (30429, '01527165', '29878', '2011/11/24', null, 21, 510490, '029') -compras values (32878, '01528771', '29878', '2011/11/28', null, 21, 1684150, '029') -compras values (9060, '01509738', '29879', '2011/10/24', null, 21, 424830, '029') -compras values (19877, '01518876', '29879', '2011/11/09', null, 21, 356960, '029') -compras values (5077, '01507317', '29922', '2011/10/18', null, 21, 1684170, '029') -compras values (30432, '01527745', '29922', '2011/11/24', null, 21, 1448900, '029') -compras values (32882, '01529936', '29922', '2011/11/29', null, 21, 1282750, '029') -compras values (14137, '02514315', '29922', '2011/10/31', null, 21, 537120, '029') -compras values (5170, '01502744', '30159', '2011/10/10', null, 21, 245610, '029') -compras values (5169, '01506279', '30159', '2011/10/17', null, 21, 174170, '029') -compras values (5168, '01509267', '30159', '2011/10/23', null, 21, 404020, '029') -compras values (9087, '01509885', '30159', '2011/10/24', null, 21, 232740, '029') -compras values (11816, '01512997', '30159', '2011/10/30', null, 21, 365980, '029') -compras values (15860, '01515529', '30159', '2011/11/03', null, 21, 274340, '029') -compras values (25338, '01523423', '30159', '2011/11/17', null, 21, 233500, '029') -compras values (27694, '01525133', '30159', '2011/11/21', null, 21, 178420, '029') -compras values (31441, '01528258', '30159', '2011/11/27', null, 21, 360060, '029') -compras values (14299, '01513385', '31271', '2011/10/31', null, 14, 647080, '029') -compras values (5577, '01506280', '31292', '2011/10/17', null, 10, 566980, '029') -compras values (5576, '01507614', '31292', '2011/10/19', null, 10, 124000, '029') -compras values (5575, '01509270', '31292', '2011/10/23', null, 10, 479270, '029') -compras values (9171, '01510631', '31292', '2011/10/25', null, 10, 573660, '029') -compras values (11922, '01512701', '31292', '2011/10/28', null, 10, 576560, '029') -compras values (14301, '01513372', '31292', '2011/10/31', null, 10, 310330, '029') -compras values (14300, '01514920', '31292', '2011/11/02', null, 10, 620040, '029') -compras values (15923, '01515534', '31292', '2011/11/03', null, 10, 704060, '029') -compras values (16654, '01516540', '31292', '2011/11/04', null, 10, 161680, '029') -compras values (17145, '01516933', '31292', '2011/11/06', null, 10, 494560, '029') -compras values (19020, '01518263', '31292', '2011/11/08', null, 10, 489720, '029') -compras values (21030, '01519759', '31292', '2011/11/10', null, 10, 607080, '029') -compras values (25449, '01522693', '31292', '2011/11/16', null, 10, 528960, '029') -compras values (25448, '01522695', '31292', '2011/11/16', null, 10, 486780, '029') -compras values (27735, '01525136', '31292', '2011/11/21', null, 10, 665970, '029') -compras values (30500, '01527171', '31292', '2011/11/24', null, 10, 647830, '029') -compras values (31485, '01527929', '31292', '2011/11/25', null, 10, 643740, '029') -compras values (11923, '01513206', '31314', '2011/10/30', null, 10, 415800, '029') -compras values (5649, '01508740', '31503', '2011/10/20', null, 15, 181720, '029') -compras values (18022, '01517930', '31503', '2011/11/07', null, 21, 844480, '029') -compras values (5657, '01509298', '31541', '2011/10/23', null, 10, 205650, '029') -compras values (5869, '01505903', '32020', '2011/10/16', null, 9, 1136880, '029') -compras values (5868, '01508214', '32020', '2011/10/20', null, 9, 753840, '029') -compras values (5867, '01508215', '32020', '2011/10/20', null, 9, 2331660, '029') -compras values (12001, '01513011', '32020', '2011/10/30', null, 10, 1117940, '029') -compras values (12000, '01513012', '32020', '2011/10/30', null, 10, 3229690, '029') -compras values (17167, '01516842', '32020', '2011/11/06', null, 10, 3555090, '029') -compras values (17166, '01516843', '32020', '2011/11/06', null, 10, 1217560, '029') -compras values (25536, '01522706', '32020', '2011/11/16', null, 9, 814600, '029') -compras values (25535, '01522707', '32020', '2011/11/16', null, 10, 1858280, '029') -compras values (29525, '01526456', '32020', '2011/11/23', null, 10, 2009900, '029') -compras values (29524, '01526457', '32020', '2011/11/23', null, 10, 531990, '029') -compras values (33046, '01529501', '32020', '2011/11/29', null, 10, 4000580, '029') -compras values (33045, '01529502', '32020', '2011/11/29', null, 10, 868140, '029') -compras values (6051, '01505235', '32313', '2011/10/13', null, 14, 526410, '029') -compras values (6050, '01505716', '32313', '2011/10/14', null, 14, 224000, '029') -compras values (6049, '01509112', '32313', '2011/10/21', null, 14, 285460, '029') -compras values (18083, '01517941', '32313', '2011/11/07', null, 14, 204900, '029') -compras values (20009, '01519479', '32313', '2011/11/09', null, 14, 352180, '029') -compras values (28723, '01526255', '32313', '2011/11/22', null, 14, 388380, '029') -compras values (31526, '01528163', '32313', '2011/11/25', null, 14, 233800, '029') -compras values (33092, '01529898', '32313', '2011/11/29', null, 14, 143130, '029') -compras values (14538, '01514724', '32730', '2011/11/01', null, 21, 403920, '029') -compras values (19113, '01518399', '32730', '2011/11/08', null, 21, 168000, '029') -compras values (23499, '01521654', '32730', '2011/11/14', null, 21, 278360, '029') -compras values (23498, '01521655', '32730', '2011/11/14', null, 21, 371390, '029') -compras values (23497, '01522086', '32730', '2011/11/15', null, 21, 462000, '029') -compras values (25643, '01523557', '32730', '2011/11/17', null, 21, 231550, '029') -compras values (27815, '01525254', '32730', '2011/11/21', null, 21, 661330, '029') -compras values (28751, '01525991', '32730', '2011/11/22', null, 21, 82800, '029') -compras values (6266, '01504709', '32749', '2011/10/13', null, 28, 509800, '029') -compras values (12093, '01511961', '32749', '2011/10/27', null, 28, 344500, '029') -compras values (16035, '01515531', '32749', '2011/11/03', null, 28, 1940400, '029') -compras values (16689, '01516544', '32749', '2011/11/04', null, 28, 73990, '029') -compras values (18111, '01517444', '32749', '2011/11/07', null, 28, 283100, '029') -compras values (30596, '01527167', '32749', '2011/11/24', null, 28, 578600, '029') -compras values (6267, '01504592', '32751', '2011/10/13', null, 28, 557300, '029') -compras values (9343, '01509726', '32751', '2011/10/24', null, 28, 1041930, '029') -compras values (12094, '01511845', '32751', '2011/10/27', null, 28, 239200, '029') -compras values (16036, '01515667', '32751', '2011/11/03', null, 28, 1691910, '029') -compras values (16690, '01516420', '32751', '2011/11/04', null, 28, 245870, '029') -compras values (18112, '01517239', '32751', '2011/11/07', null, 28, 141550, '029') -compras values (21152, '01519640', '32751', '2011/11/10', null, 28, 143070, '029') -compras values (30597, '01527068', '32751', '2011/11/24', null, 28, 459000, '029') -compras values (6307, '01505362', '32836', '2011/10/13', null, 10, 384000, '029') -compras values (6306, '01509141', '32836', '2011/10/21', null, 10, 350600, '029') -compras values (12102, '01512590', '32836', '2011/10/27', null, 10, 316800, '029') -compras values (16041, '01516258', '32836', '2011/11/03', null, 10, 376000, '029') -compras values (19122, '01518828', '32836', '2011/11/08', null, 10, 436290, '029') -compras values (23510, '01520823', '32836', '2011/11/11', null, 10, 439100, '029') -compras values (25661, '01523198', '32836', '2011/11/16', null, 10, 345600, '029') -compras values (27822, '01525690', '32836', '2011/11/21', null, 10, 399600, '029') -compras values (31550, '01528149', '32836', '2011/11/25', null, 10, 543670, '029') -compras values (33153, '01529983', '32836', '2011/11/29', null, 10, 384000, '029') -compras values (6410, '01504710', '33015', '2011/10/13', null, 10, 240950, '029') -compras values (12120, '01511969', '33015', '2011/10/27', null, 10, 501680, '029') -compras values (18133, '01517452', '33015', '2011/11/07', null, 10, 320480, '029') -compras values (6477, '01507410', '33159', '2011/10/18', null, 14, 215800, '029') -compras values (6476, '01507983', '33159', '2011/10/19', null, 14, 332300, '029') -compras values (16701, '01516799', '33159', '2011/11/04', null, 14, 290610, '029') -compras values (20057, '01519540', '33159', '2011/11/09', null, 14, 218820, '029') -compras values (6568, '01503544', '33334', '2011/10/11', null, 21, 1346380, '029') -compras values (9430, '01509887', '33335', '2011/10/24', null, 21, 651580, '029') -compras values (9429, '01510629', '33335', '2011/10/25', null, 21, 185550, '029') -compras values (6571, '01503563', '33336', '2011/10/11', null, 21, 154800, '029') -compras values (6570, '01504630', '33336', '2011/10/13', null, 21, 228900, '029') -compras values (6569, '01506805', '33336', '2011/10/18', null, 21, 754150, '029') -compras values (9431, '01510543', '33336', '2011/10/25', null, 21, 144960, '029') -compras values (19160, '01518193', '33336', '2011/11/08', null, 21, 399340, '029') -compras values (6574, '01506283', '33337', '2011/10/17', null, 21, 1637400, '029') -compras values (6573, '01506284', '33337', '2011/10/17', null, 20, 2291120, '029') -compras values (6572, '01506285', '33337', '2011/10/17', null, 20, 606340, '029') -compras values (9432, '01509890', '33337', '2011/10/24', null, 21, 2897800, '029') -compras values (14635, '01513380', '33337', '2011/10/31', null, 21, 2934200, '029') -compras values (18150, '01517453', '33337', '2011/11/07', null, 21, 318000, '029') -compras values (18149, '01517454', '33337', '2011/11/07', null, 21, 1726300, '029') -compras values (26887, '01524283', '33337', '2011/11/18', null, 21, 1102800, '029') -compras values (6603, '01505893', '33395', '2011/10/16', null, 10, 248000, '029') -compras values (6602, '01508175', '33395', '2011/10/20', null, 10, 486150, '029') -compras values (6601, '01509266', '33395', '2011/10/23', null, 10, 736000, '029') -compras values (12167, '01511956', '33395', '2011/10/27', null, 10, 520130, '029') -compras values (16076, '01515524', '33395', '2011/11/03', null, 10, 321240, '029') -compras values (19166, '01518258', '33395', '2011/11/08', null, 10, 458700, '029') -compras values (23590, '01522001', '33395', '2011/11/15', null, 10, 705410, '029') -compras values (25733, '01523443', '33395', '2011/11/17', null, 10, 476400, '029') -compras values (26892, '01524628', '33395', '2011/11/20', null, 10, 1265400, '029') -compras values (31573, '01527932', '33395', '2011/11/25', null, 10, 487560, '029') -compras values (33210, '01528778', '33395', '2011/11/28', null, 10, 181230, '029') -compras values (6669, '01503947', '33494', '2011/10/12', null, 10, 248140, '029') -compras values (14670, '01514923', '33494', '2011/11/02', null, 10, 255970, '029') -compras values (25753, '01522686', '33494', '2011/11/16', null, 10, 222350, '029') -compras values (31580, '01528261', '33494', '2011/11/27', null, 10, 117000, '029') -compras values (7111, '01504767', '34072', '2011/10/13', null, 28, 1022630, '029') -compras values (12280, '01512046', '34072', '2011/10/27', null, 28, 119600, '029') -compras values (16151, '01515576', '34072', '2011/11/03', null, 28, 1171170, '029') -compras values (30690, '01527205', '34072', '2011/11/24', null, 28, 546600, '029') -compras values (7252, '01502817', '34302', '2011/10/10', null, 10, 456620, '029') -compras values (7251, '01505636', '34302', '2011/10/14', null, 10, 485520, '029') -compras values (16171, '01515548', '34302', '2011/11/03', null, 10, 805250, '029') -compras values (7296, '01506741', '34361', '2011/10/18', null, 10, 201650, '029') -compras values (7295, '01508892', '34361', '2011/10/21', null, 10, -800, '029') -compras values (7294, '01509218', '34361', '2011/10/23', null, 10, 406840, '029') -compras values (10469, '01511211', '34361', '2011/10/26', null, 10, 322530, '029') -compras values (14875, '01513403', '34361', '2011/10/31', null, 10, 797780, '029') -compras values (33354, '01528632', '34361', '2011/11/28', null, 10, 103600, '029') -compras values (7526, '01504712', '34686', '2011/10/13', null, 14, 372840, '029') -compras values (7525, '01505571', '34686', '2011/10/14', null, 14, 291690, '029') -compras values (7524, '01508921', '34686', '2011/10/21', null, 14, 506840, '029') -compras values (14948, '01513375', '34686', '2011/10/31', null, 14, 2396810, '029') -compras values (12437, '01511980', '35005', '2011/10/27', null, 7, 71960, '029') -compras values (16766, '01515533', '35027', '2011/11/03', null, 10, 265370, '029') -compras values (27004, '01524633', '35027', '2011/11/20', null, 10, 212400, '029') -receber values(11336, '00124', '01510441', '2011/10/24', '2011/11/07', 383180, 383180, 0, 'NF', 'KXH4906', 'B', null, '029') -receber values(28070, '00124', '01525686', '2011/11/21', '2011/12/05', 762680, 762680, 0, 'NF', 'LSX0479', 'B', null, '029') -receber values(34006, '00124', '01529274', '2011/11/28', '2011/12/12', 344640, 344640, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(16278, '00508', '01514767', '2011/11/02', '2011/11/16', 209310, 209310, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21836, '00508', '01517118', '2011/11/06', '2011/11/20', 101100, 101100, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(28071, '00508', '01525594', '2011/11/21', '2011/12/05', 191230, 191230, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(28072, '00508', '01526256', '2011/11/22', '2011/12/06', 587160, 587160, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(32156, '00508', '01527065', '2011/11/24', '2011/12/08', 209800, 209800, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(6403, '01201', '01505227', '2011/10/13', '2011/11/03', 1897530, 1897530, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6404, '01201', '01507337', '2011/10/18', '2011/11/08', 509080, 509080, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6405, '01201', '01508742', '2011/10/20', '2011/11/10', 954520, 954520, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(16279, '01201', '01515269', '2011/11/02', '2011/11/23', 248800, 248800, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21837, '01201', '01516124', '2011/11/03', '2011/11/24', 674530, 674530, 0, 'NF', 'LJN6251', 'B', null, '029') -receber values(21838, '01201', '01518705', '2011/11/08', '2011/11/29', 1017860, 1017860, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(28073, '01201', '01523147', '2011/11/16', '2011/12/07', 629500, 629500, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(31243, '01201', '01526987', '2011/11/23', '2011/12/14', 374360, 374360, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(28074, '01219', '01524079', '2011/11/17', '2011/11/24', 634400, 634400, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6406, '02176', '01506292', '2011/10/17', '2011/10/27', 600320, 600320, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(16280, '02176', '01513471', '2011/10/31', '2011/11/10', 649040, 649040, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(21839, '02176', '01521584', '2011/11/14', '2011/11/24', 811910, 811910, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(6407, '02528', '01506291', '2011/10/17', '2011/10/27', 563200, 563200, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(11337, '02528', '01509902', '2011/10/24', '2011/11/03', 532860, 532860, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(21840, '02528', '01517481', '2011/11/07', '2011/11/17', 486560, 486560, 0, 'NF', 'KCC1472', 'B', null, '029') -receber values(21841, '02528', '01521583', '2011/11/14', '2011/11/24', 382740, 382740, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(28075, '02528', '01525097', '2011/11/21', '2011/12/01', 490550, 490550, 0, 'NF', 'LRA0472', 'T', null, '029') -receber values(34007, '02528', '01528785', '2011/11/28', '2011/12/08', 367610, 367610, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(11338, '02602', '01511148', '2011/10/25', '2011/11/04', 399400, 409390, 9990, 'NF', 'LCP3836', 'B', null, '029') -receber values(11339, '02604', '01509903', '2011/10/24', '2011/11/03', 211600, 211600, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(16281, '02604', '01514597', '2011/11/01', '2011/11/11', 429630, 429630, 0, 'NF', 'CXV9668', 'B', null, '029') -receber values(28076, '02604', '01525098', '2011/11/21', '2011/12/01', 317200, 317200, 0, 'NF', 'LRA0472', 'T', null, '029') -receber values(1443, '02609', '01491322', '2011/09/19', '2011/09/24', 389720, 448180, 58460, 'NF', 'LUP0560', 'B', null, '029') -receber values(11340, '02609', '01510607', '2011/10/25', '2011/10/30', 279000, 284580, 5580, 'NF', 'KMM5715', 'B', null, '029') -receber values(16282, '02609', '01513470', '2011/10/31', '2011/11/05', 323140, 329600, 6460, 'NF', 'CXV9668', 'B', null, '029') -receber values(18897, '02609', '01517480', '2011/11/07', '2011/11/12', 319460, 329040, 9580, 'NF', 'KCC1472', 'B', null, '029') -receber values(21842, '02609', '01521582', '2011/11/14', '2011/11/19', 416850, 423100, 6250, 'NF', 'LUP0560', 'B', null, '029') -receber values(28077, '02609', '01525096', '2011/11/21', '2011/11/26', 429970, 429970, 0, 'NF', 'LRA0472', 'T', null, '029') -receber values(34008, '02609', '01528784', '2011/11/28', '2011/12/03', 535210, 535210, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(34942, '02609', '01529500', '2011/11/29', '2011/12/04', 71820, 71820, 0, 'NF', 'KCC1472', 'T', null, '029') -receber values(35866, '02609', '01530123', '2011/11/30', '2011/12/05', 164980, 164980, 0, 'NF', 'KCC1472', 'T', null, '029') -receber values(6408, '02611', '01507628', '2011/10/19', '2011/11/02', 153260, 154030, 770, 'NF', 'KMM5715', 'B', null, '029') -receber values(11341, '02611', '01509904', '2011/10/24', '2011/11/07', 216880, 216880, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(21843, '02611', '01515546', '2011/11/03', '2011/11/17', 130660, 130660, 0, 'NF', 'CXV9668', 'B', null, '029') -receber values(21844, '02611', '01517479', '2011/11/07', '2011/11/21', 191180, 191180, 0, 'NF', 'KCC1472', 'B', null, '029') -receber values(28078, '02611', '01525099', '2011/11/21', '2011/12/05', 190270, 190270, 0, 'NF', 'LRA0472', 'T', null, '029') -receber values(34009, '02611', '01528786', '2011/11/28', '2011/12/12', 225400, 225400, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(6409, '03884', '01505758', '2011/10/14', '2011/10/28', 148050, 148050, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(6410, '03884', '01508842', '2011/10/20', '2011/11/03', 557800, 557800, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(16283, '03884', '01515264', '2011/11/02', '2011/11/16', 405800, 405800, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(21845, '03884', '01520810', '2011/11/11', '2011/11/25', 607570, 607570, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(28079, '03884', '01524015', '2011/11/17', '2011/12/01', 629500, 629500, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(32157, '03884', '01527783', '2011/11/24', '2011/12/08', 650260, 650260, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(6411, '04195', '01501471', '2011/10/06', '2011/10/25', 379280, 379280, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(18898, '04195', '01516115', '2011/11/03', '2011/11/13', 562830, 571270, 8440, 'NF', 'LJN6251', 'B', null, '029') -receber values(21846, '04195', '01521735', '2011/11/14', '2011/11/24', 248870, 248870, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(28080, '04195', '01523137', '2011/11/16', '2011/11/26', 679950, 679950, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(31244, '04195', '01526981', '2011/11/23', '2011/12/03', 221280, 221280, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(33195, '04195', '01528155', '2011/11/25', '2011/12/05', 227420, 227420, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(1444, '04750', '01497742', '2011/09/29', '2011/10/12', 524320, 561020, 36700, 'NF', 'LCP3836', 'B', null, '029') -receber values(1445, '04750', '01497743', '2011/09/29', '2011/10/13', 618200, 658380, 40180, 'NF', 'LCP3836', 'B', null, '029') -receber values(1446, '04750', '01501635', '2011/10/06', '2011/10/19', 898800, 966210, 67410, 'NF', 'LCP3836', 'B', null, '029') -receber values(1447, '04750', '01501636', '2011/10/06', '2011/10/20', 781400, 836100, 54700, 'NF', 'LCP3836', 'B', null, '029') -receber values(1448, '04750', '01503436', '2011/10/10', '2011/10/23', 834380, 880270, 45890, 'NF', 'LGY8427', 'B', null, '029') -receber values(6412, '04750', '01503437', '2011/10/10', '2011/10/24', 854800, 923180, 68380, 'NF', 'LGY8427', 'B', null, '029') -receber values(6413, '04750', '01505388', '2011/10/13', '2011/10/26', 973300, 1041430, 68130, 'NF', 'LCP3836', 'B', null, '029') -receber values(6414, '04750', '01505389', '2011/10/13', '2011/10/27', 1016820, 1082910, 66090, 'NF', 'LCP3836', 'B', null, '029') -receber values(6415, '04750', '01507436', '2011/10/18', '2011/10/31', 1061090, 1108840, 47750, 'NF', 'LCP3836', 'B', null, '029') -receber values(6416, '04750', '01507437', '2011/10/18', '2011/11/01', 832950, 866270, 33320, 'NF', 'LCP3836', 'B', null, '029') -receber values(11342, '04750', '01510420', '2011/10/24', '2011/11/06', 901070, 914590, 13520, 'NF', 'LCP3836', 'B', null, '029') -receber values(11343, '04750', '01510421', '2011/10/24', '2011/11/07', 940410, 949810, 9400, 'NF', 'LCP3836', 'B', null, '029') -receber values(12848, '04750', '01511765', '2011/10/26', '2011/11/08', 961600, 966410, 4810, 'NF', 'LCP3836', 'B', null, '029') -receber values(12849, '04750', '01511766', '2011/10/26', '2011/11/09', 1024830, 1029950, 5120, 'NF', 'LCP3836', 'T', null, '029') -receber values(21847, '04750', '01516278', '2011/11/03', '2011/11/16', 799100, 823070, 23970, 'NF', 'LCP3836', 'T', null, '029') -receber values(21848, '04750', '01516279', '2011/11/03', '2011/11/17', 810400, 830660, 20260, 'NF', 'LCP3836', 'T', null, '029') -receber values(21849, '04750', '01516755', '2011/11/04', '2011/11/18', 1029500, 1050090, 20590, 'NF', 'LCP3836', 'T', null, '029') -receber values(21850, '04750', '01520426', '2011/11/10', '2011/11/21', 728300, 731940, 3640, 'NF', 'LGY8427', 'T', null, '029') -receber values(21851, '04750', '01520427', '2011/11/10', '2011/11/23', 923230, 923230, 0, 'NF', 'LGY8427', 'T', null, '029') -receber values(21852, '04750', '01520428', '2011/11/10', '2011/11/24', 847100, 847100, 0, 'NF', 'LGY8427', 'T', null, '029') -receber values(28081, '04750', '01524021', '2011/11/17', '2011/12/01', 961670, 961670, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(28082, '04750', '01524022', '2011/11/17', '2011/11/30', 979550, 979550, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(34943, '04750', '01529976', '2011/11/29', '2011/12/12', 964840, 964840, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(34944, '04750', '01529977', '2011/11/29', '2011/12/13', 795570, 795570, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(13719, '07017', '01512467', '2011/10/27', '2011/11/10', 371160, 371160, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21853, '07017', '01516134', '2011/11/03', '2011/11/17', 364520, 364520, 0, 'NF', 'LJN6251', 'B', null, '029') -receber values(28083, '07017', '01523161', '2011/11/16', '2011/11/30', 574420, 574420, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(13720, '07045', '01511970', '2011/10/27', '2011/11/03', 155400, 155400, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(31245, '07508', '01526626', '2011/11/23', '2011/12/03', 210580, 210580, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(35867, '07605', '01530116', '2011/11/30', '2011/12/10', 270800, 270800, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(6417, '07638', '01503686', '2011/10/11', '2011/11/01', 450080, 450080, 0, 'NF', 'KXH2344', 'B', null, '029') -receber values(21854, '07638', '01518400', '2011/11/08', '2011/11/29', 182600, 182600, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(21855, '07638', '01519892', '2011/11/10', '2011/12/01', 164150, 164150, 0, 'NF', 'KCC1472', 'T', null, '029') -receber values(21856, '07638', '01521658', '2011/11/14', '2011/12/05', 283880, 283880, 0, 'NF', 'LRX2193', 'T', null, '029') -receber values(28084, '07638', '01523559', '2011/11/17', '2011/12/08', 604520, 604520, 0, 'NF', 'KXH2344', 'T', null, '029') -receber values(28085, '07638', '01525258', '2011/11/21', '2011/12/12', 530420, 530420, 0, 'NF', 'KMN4639', 'T', null, '029') -receber values(34010, '07638', '01528866', '2011/11/28', '2011/12/19', 322790, 322790, 0, 'NF', 'KMN4639', 'T', null, '029') -receber values(34945, '07638', '01529619', '2011/11/29', '2011/12/20', 150170, 150170, 0, 'NF', 'KXH2344', 'T', null, '029') -receber values(1449, '07799', '01499547', '2011/10/04', '2011/10/14', 125260, 131520, 6260, 'NF', 'KCC1472', 'B', null, '029') -receber values(1450, '07799', '01502818', '2011/10/10', '2011/10/20', 112160, 118330, 6170, 'NF', 'CXV9668', 'B', null, '029') -receber values(6418, '07799', '01506290', '2011/10/17', '2011/10/27', 124320, 128670, 4350, 'NF', 'LUP0560', 'B', null, '029') -receber values(11344, '07799', '01509901', '2011/10/24', '2011/11/03', 121760, 125410, 3650, 'NF', 'LUP0560', 'B', null, '029') -receber values(16284, '07799', '01513469', '2011/10/31', '2011/11/10', 122090, 126970, 4880, 'NF', 'CXV9668', 'B', null, '029') -receber values(21857, '07799', '01521581', '2011/11/14', '2011/11/24', 124220, 124220, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(28086, '07799', '01525095', '2011/11/21', '2011/12/01', 128840, 128840, 0, 'NF', 'LRA0472', 'T', null, '029') -receber values(34011, '07799', '01528783', '2011/11/28', '2011/12/08', 148200, 148200, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(6419, '07920', '01503737', '2011/10/11', '2011/11/01', 880390, 880390, 0, 'NF', 'LCE4302', 'B', null, '029') -receber values(6420, '07920', '01506046', '2011/10/16', '2011/10/31', 168110, 168110, 0, 'NF', 'KUV3781', 'B', null, '029') -receber values(21858, '07920', '01516706', '2011/11/04', '2011/11/25', 715820, 715820, 0, 'NF', 'LCE4302', 'B', null, '029') -receber values(21859, '07920', '01521762', '2011/11/14', '2011/12/05', 865450, 865450, 0, 'NF', 'KRB6158', 'T', null, '029') -receber values(28087, '07920', '01525633', '2011/11/21', '2011/12/06', 126820, 126820, 0, 'NF', 'LCE4302', 'T', null, '029') -receber values(16285, '07920', '02514311', '2011/10/31', '2011/11/21', 811760, 811760, 0, 'NF', 'LCE4302', 'B', null, '029') -receber values(6421, '07921', '01498715', '2011/10/03', '2011/10/24', 132860, 132860, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6422, '07921', '01502731', '2011/10/10', '2011/10/31', 329830, 329830, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6423, '07921', '01504706', '2011/10/13', '2011/10/29', 161240, 162850, 1610, 'NF', 'KYK5632', 'B', null, '029') -receber values(6424, '07921', '01506271', '2011/10/17', '2011/11/07', 296870, 296870, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(11345, '07921', '01509881', '2011/10/24', '2011/11/14', 226870, 229140, 2270, 'NF', 'KYK5632', 'B', null, '029') -receber values(11346, '07921', '01510625', '2011/10/25', '2011/11/15', 302400, 303910, 1510, 'NF', 'KYK5632', 'B', null, '029') -receber values(13721, '07921', '01511953', '2011/10/27', '2011/11/17', 645160, 645160, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16286, '07921', '01513363', '2011/10/31', '2011/11/21', 292680, 292680, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16287, '07921', '01514570', '2011/11/01', '2011/11/22', 330800, 330800, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21860, '07921', '01515525', '2011/11/03', '2011/11/24', 198740, 198740, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21861, '07921', '01517435', '2011/11/07', '2011/11/28', 316210, 316210, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21862, '07921', '01519750', '2011/11/10', '2011/12/01', 324510, 324510, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(21863, '07921', '01520581', '2011/11/11', '2011/12/02', 372270, 372270, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(21864, '07921', '01521570', '2011/11/14', '2011/12/05', 211680, 211680, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28088, '07921', '01525128', '2011/11/21', '2011/12/12', 612440, 612440, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34012, '07921', '01528777', '2011/11/28', '2011/12/19', 414720, 414720, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1451, '09732', '05244472', '2011/10/12', '2011/10/22', 82400, 83220, 820, 'NF', 'KYK5632', 'B', null, '029') -receber values(6425, '09732', '05245440', '2011/10/18', '2011/10/28', 120690, 124310, 3620, 'NF', 'KYK5632', 'B', null, '029') -receber values(11347, '09732', '05246738', '2011/10/25', '2011/11/04', 82400, 84460, 2060, 'NF', 'KYK5632', 'B', null, '029') -receber values(21865, '09732', '05249600', '2011/11/09', '2011/11/19', 84100, 84100, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28089, '09732', '05250894', '2011/11/16', '2011/11/26', 89000, 89000, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28090, '09732', '05251981', '2011/11/22', '2011/12/02', 163450, 163450, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1452, '09836', '01502733', '2011/10/10', '2011/10/20', 370800, 407880, 37080, 'NF', 'KYK5632', 'B', null, '029') -receber values(6426, '09836', '01506881', '2011/10/18', '2011/10/28', 208460, 220970, 12510, 'NF', 'KYK5632', 'B', null, '029') -receber values(11348, '09836', '01509880', '2011/10/24', '2011/11/03', 554290, 570920, 16630, 'NF', 'KYK5632', 'B', null, '029') -receber values(21866, '09836', '01518259', '2011/11/08', '2011/11/18', 967990, 987350, 19360, 'NF', 'KYK5632', 'T', null, '029') -receber values(28091, '09836', '01525129', '2011/11/21', '2011/12/01', 464450, 464450, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(33196, '09836', '01528260', '2011/11/27', '2011/12/07', 349010, 349010, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34013, '09836', '01528776', '2011/11/28', '2011/12/08', 700640, 700640, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1453, '10382', '01495067', '2011/09/26', '2011/10/17', 1653000, 1793510, 140510, 'NF', 'CXV9668', 'B', null, '029') -receber values(6427, '10382', '01499429', '2011/10/04', '2011/10/24', 1194600, 1194600, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(6428, '10382', '01499430', '2011/10/04', '2011/10/25', 1717160, 1768670, 51510, 'NF', 'KYV1613', 'B', null, '029') -receber values(6429, '10382', '01501704', '2011/10/07', '2011/10/28', 1811860, 1866220, 54360, 'NF', 'KMM5715', 'B', null, '029') -receber values(6430, '10382', '01503565', '2011/10/11', '2011/11/01', 352000, 352000, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(6431, '10382', '01504632', '2011/10/13', '2011/11/03', 123420, 123420, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(6432, '10382', '01506179', '2011/10/17', '2011/11/07', 1944500, 1963950, 19450, 'NF', 'KYV1613', 'B', null, '029') -receber values(6433, '10382', '01508103', '2011/10/20', '2011/11/10', 317200, 326720, 9520, 'NF', 'KYV1613', 'B', null, '029') -receber values(11349, '10382', '01510545', '2011/10/25', '2011/11/15', 354450, 366860, 12410, 'NF', 'KYV1613', 'T', null, '029') -receber values(13722, '10382', '01511889', '2011/10/27', '2011/11/17', 490550, 502810, 12260, 'NF', 'KYV1613', 'B', null, '029') -receber values(16288, '10382', '01513276', '2011/10/31', '2011/11/21', 392390, 392390, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(21867, '10382', '01516440', '2011/11/04', '2011/11/25', 850680, 850680, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(21868, '10382', '01518195', '2011/11/08', '2011/11/29', 773800, 773800, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(21869, '10382', '01519688', '2011/11/10', '2011/12/01', 81600, 81600, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(28092, '10382', '01522611', '2011/11/16', '2011/12/07', 789530, 789530, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(28093, '10382', '01525825', '2011/11/22', '2011/12/13', 434000, 434000, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(34014, '10382', '01528721', '2011/11/28', '2011/12/19', 200750, 200750, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(34946, '10382', '01529396', '2011/11/29', '2011/12/20', 331500, 331500, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(6434, '10737', '01509302', '2011/10/23', '2011/11/02', 200860, 201860, 1000, 'NF', 'KXH4906', 'B', null, '029') -receber values(35868, '10737', '01530226', '2011/11/30', '2011/12/10', 135800, 135800, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(6435, '13087', '01498688', '2011/10/03', '2011/10/24', 520190, 520190, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(6436, '13087', '01506248', '2011/10/17', '2011/11/07', 756000, 756000, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(6437, '13087', '01509386', '2011/10/23', '2011/11/13', 395300, 401230, 5930, 'NF', 'LSL2615', 'B', null, '029') -receber values(11350, '13087', '01509853', '2011/10/24', '2011/11/14', 288040, 290920, 2880, 'NF', 'LSL2615', 'B', null, '029') -receber values(12850, '13087', '01511315', '2011/10/26', '2011/11/16', 402250, 402250, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(14755, '13087', '01512975', '2011/10/30', '2011/11/20', 618560, 618560, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(16289, '13087', '01513443', '2011/10/31', '2011/11/21', 704180, 704180, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(21870, '13087', '01517420', '2011/11/07', '2011/11/28', 566740, 566740, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(21871, '13087', '01520919', '2011/11/13', '2011/12/04', 590840, 590840, 0, 'NF', 'LSL2615', 'T', null, '029') -receber values(21872, '13087', '01521508', '2011/11/14', '2011/12/05', 552390, 552390, 0, 'NF', 'LSL2615', 'T', null, '029') -receber values(28094, '13087', '01525053', '2011/11/21', '2011/12/12', 520680, 520680, 0, 'NF', 'LSL2615', 'T', null, '029') -receber values(34015, '13087', '01528703', '2011/11/28', '2011/12/19', 537380, 537380, 0, 'NF', 'LSL2615', 'T', null, '029') -receber values(6438, '14100', '05243990', '2011/10/10', '2011/10/24', 322330, 322330, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6439, '14100', '05245438', '2011/10/18', '2011/11/01', 399880, 403880, 4000, 'NF', 'KYK5632', 'B', null, '029') -receber values(16290, '14100', '05248263', '2011/11/02', '2011/11/16', 226700, 228970, 2270, 'NF', 'KYK5632', 'B', null, '029') -receber values(21873, '14100', '05249082', '2011/11/07', '2011/11/17', 477720, 480110, 2390, 'NF', 'KYK5632', 'B', null, '029') -receber values(21874, '14100', '05250675', '2011/11/15', '2011/11/29', 352700, 352700, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28095, '14100', '05251763', '2011/11/21', '2011/12/05', 420060, 420060, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(6440, '14770', '01502674', '2011/10/10', '2011/10/31', 165720, 165720, 0, 'NF', 'LBX9000', 'B', null, '029') -receber values(6441, '14770', '01504623', '2011/10/13', '2011/11/03', 584270, 584270, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(6442, '14770', '01508092', '2011/10/20', '2011/11/10', 370670, 370670, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(13723, '14770', '01511881', '2011/10/27', '2011/11/17', 364940, 364940, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(21875, '14770', '01518185', '2011/11/08', '2011/11/29', 172140, 172140, 0, 'NF', 'KYV1613', 'B', null, '029') -receber values(21876, '14770', '01519673', '2011/11/10', '2011/12/01', 272320, 272320, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(21877, '14770', '01521545', '2011/11/14', '2011/12/05', 251570, 251570, 0, 'NF', 'KNH3108', 'T', null, '029') -receber values(28096, '14770', '01525014', '2011/11/21', '2011/12/12', 263240, 263240, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(32158, '14770', '01527126', '2011/11/24', '2011/12/15', 141440, 141440, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(34016, '14770', '01528715', '2011/11/28', '2011/12/19', 392280, 392280, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(28097, '14778', '01523308', '2011/11/17', '2011/12/08', 369090, 369090, 0, 'NF', 'KNH3108', 'B', null, '029') -receber values(6443, '14781', '01503421', '2011/10/10', '2011/10/31', 1207580, 1207580, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(6444, '14781', '01507429', '2011/10/18', '2011/11/08', 1232800, 1232800, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(11351, '14781', '01511142', '2011/10/25', '2011/11/15', 612500, 633940, 21440, 'NF', 'LCP3836', 'T', null, '029') -receber values(21878, '14781', '01519552', '2011/11/09', '2011/11/30', 960450, 960450, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(28098, '14781', '01523205', '2011/11/16', '2011/12/07', 388700, 388700, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(16291, '14781', '02514375', '2011/10/31', '2011/11/21', 469640, 469640, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(16292, '14781', '02514387', '2011/10/31', '2011/11/21', 366140, 366140, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(1454, '14838', '01498128', '2011/10/02', '2011/10/23', 679450, 682850, 3400, 'NF', 'LQO1343', 'B', null, '029') -receber values(6445, '14838', '01498556', '2011/10/03', '2011/10/24', 292700, 292700, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6446, '14838', '01499293', '2011/10/04', '2011/10/25', 289740, 289740, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6447, '14838', '01502075', '2011/10/09', '2011/10/30', 585630, 588560, 2930, 'NF', 'LQO1343', 'B', null, '029') -receber values(6448, '14838', '01502554', '2011/10/10', '2011/10/31', 94300, 94300, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6449, '14838', '01504576', '2011/10/13', '2011/11/03', 593270, 593270, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6450, '14838', '01505802', '2011/10/16', '2011/11/06', 806930, 806930, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(11352, '14838', '01510514', '2011/10/25', '2011/11/09', 159800, 159800, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(16293, '14838', '01514781', '2011/11/02', '2011/11/23', 331540, 331540, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21879, '14838', '01515678', '2011/11/03', '2011/11/24', 656880, 656880, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21880, '14838', '01515679', '2011/11/03', '2011/11/24', 588730, 588730, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21881, '14838', '01517253', '2011/11/07', '2011/11/28', 144390, 144390, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21882, '14838', '01518137', '2011/11/08', '2011/11/29', 713460, 713460, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21883, '14838', '01519651', '2011/11/10', '2011/12/01', 332070, 332070, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(21884, '14838', '01519652', '2011/11/10', '2011/12/01', 647050, 647050, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(21885, '14838', '01520874', '2011/11/13', '2011/12/04', 189230, 189230, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(28099, '14838', '01523266', '2011/11/17', '2011/12/08', 623240, 623240, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(28100, '14838', '01524961', '2011/11/21', '2011/12/12', 297880, 297880, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(28101, '14838', '01525758', '2011/11/22', '2011/12/13', 218140, 218140, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(33197, '14838', '01528286', '2011/11/27', '2011/12/18', 180400, 180400, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(34017, '14838', '01528608', '2011/11/28', '2011/12/12', 123440, 123440, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(6451, '14905', '01502305', '2011/10/09', '2011/10/30', 189210, 190160, 950, 'NF', 'LUF0315', 'B', null, '029') -receber values(6452, '14905', '01503412', '2011/10/10', '2011/10/31', 1110330, 1110330, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(6453, '14905', '01504278', '2011/10/12', '2011/11/02', 899800, 899800, 0, 'NF', 'LBG8411', 'B', null, '029') -receber values(6454, '14905', '01504378', '2011/10/12', '2011/11/02', 1448000, 1448000, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(6455, '14905', '01504379', '2011/10/12', '2011/11/02', 521600, 524210, 2610, 'NF', 'LUF0315', 'B', null, '029') -receber values(6456, '14905', '01505285', '2011/10/13', '2011/11/03', 318800, 318800, 0, 'NF', 'LBG8411', 'B', null, '029') -receber values(6457, '14905', '01505330', '2011/10/13', '2011/11/03', 1570230, 1570230, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(6458, '14905', '01506012', '2011/10/16', '2011/11/06', 697130, 697130, 0, 'NF', 'LBG8411', 'B', null, '029') -receber values(6459, '14905', '01506689', '2011/10/17', '2011/11/07', 851700, 851700, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(6460, '14905', '01507985', '2011/10/19', '2011/11/09', 941180, 941180, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(6461, '14905', '01507986', '2011/10/19', '2011/11/09', 99800, 99800, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(6462, '14905', '01509511', '2011/10/23', '2011/11/13', 419300, 425590, 6290, 'NF', 'LUF0315', 'B', null, '029') -receber values(11353, '14905', '01510381', '2011/10/24', '2011/11/14', 137800, 139180, 1380, 'NF', 'LUF0315', 'B', null, '029') -receber values(14756, '14905', '01513227', '2011/10/30', '2011/11/20', 98800, 98800, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(16294, '14905', '01515325', '2011/11/02', '2011/11/23', 1707040, 1707040, 0, 'NF', 'LBG8411', 'B', null, '029') -receber values(16295, '14905', '01515340', '2011/11/02', '2011/11/23', 1594880, 1594880, 0, 'NF', 'LBG8411', 'B', null, '029') -receber values(21886, '14905', '01516194', '2011/11/03', '2011/11/24', 110560, 110560, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(21887, '14905', '01516214', '2011/11/03', '2011/11/24', 189800, 189800, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(21888, '14905', '01517166', '2011/11/06', '2011/11/27', 147800, 147800, 0, 'NF', 'LUF0315', 'B', null, '029') -receber values(21889, '14905', '01519449', '2011/11/09', '2011/11/30', 147800, 147800, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(21890, '14905', '01519513', '2011/11/09', '2011/11/30', 1347800, 1347800, 0, 'NF', 'LBG8411', 'T', null, '029') -receber values(21891, '14905', '01522443', '2011/11/15', '2011/12/06', 82940, 82940, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(28102, '14905', '01523136', '2011/11/16', '2011/12/07', 1726100, 1726100, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(28103, '14905', '01523997', '2011/11/17', '2011/12/08', 1005920, 1005920, 0, 'NF', 'LBG8411', 'T', null, '029') -receber values(28104, '14905', '01524085', '2011/11/17', '2011/12/08', 147800, 147800, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(31246, '14905', '01526952', '2011/11/23', '2011/12/14', 1616300, 1616300, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(33198, '14905', '01528496', '2011/11/27', '2011/12/18', 398700, 398700, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(34947, '14905', '01529951', '2011/11/29', '2011/12/20', 57100, 57100, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(34948, '14905', '01529952', '2011/11/29', '2011/12/20', 170800, 170800, 0, 'NF', 'LBG8411', 'T', null, '029') -receber values(6463, '15127', '01500884', '2011/10/06', '2011/10/27', 359200, 359200, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(6464, '15127', '01501661', '2011/10/07', '2011/10/28', 214640, 214640, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(6465, '15127', '01502040', '2011/10/09', '2011/10/30', 376090, 377970, 1880, 'NF', 'LSL2615', 'B', null, '029') -receber values(13724, '15127', '01511919', '2011/10/27', '2011/11/11', 170120, 170120, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(31247, '15127', '01526591', '2011/11/23', '2011/12/14', 965940, 965940, 0, 'NF', 'LSL2615', 'T', null, '029') -receber values(11354, '15733', '01509884', '2011/10/24', '2011/11/14', 726750, 734020, 7270, 'NF', 'KYK5632', 'B', null, '029') -receber values(32159, '15733', '01527164', '2011/11/24', '2011/12/15', 985130, 985130, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1455, '16111', '01498247', '2011/10/02', '2011/10/23', 281380, 282790, 1410, 'NF', 'LBT3689', 'B', null, '029') -receber values(6466, '16111', '01500948', '2011/10/06', '2011/10/27', 452760, 452760, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6467, '16111', '01502144', '2011/10/09', '2011/10/30', 177370, 178260, 890, 'NF', 'LBT3689', 'B', null, '029') -receber values(6468, '16111', '01504768', '2011/10/13', '2011/11/03', 497420, 497420, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6469, '16111', '01505587', '2011/10/14', '2011/11/04', 497810, 497810, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6470, '16111', '01505909', '2011/10/16', '2011/11/06', 1621610, 1621610, 0, 'NF', 'KMI7446', 'B', null, '029') -receber values(6471, '16111', '01508200', '2011/10/20', '2011/11/04', 139160, 139160, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6472, '16111', '01509288', '2011/10/23', '2011/11/13', 165860, 168350, 2490, 'NF', 'LBT3689', 'B', null, '029') -receber values(13725, '16111', '01512047', '2011/10/27', '2011/11/11', 222040, 222040, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(14757, '16111', '01513017', '2011/10/30', '2011/11/20', 1368210, 1368210, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(16296, '16111', '01513707', '2011/10/31', '2011/11/21', 277320, 277320, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(16297, '16111', '01514909', '2011/11/02', '2011/11/23', 187290, 187290, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21892, '16111', '01515575', '2011/11/03', '2011/11/24', 230080, 230080, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21893, '16111', '01516849', '2011/11/06', '2011/11/27', 261370, 261370, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21894, '16111', '01519776', '2011/11/10', '2011/12/01', 269380, 269380, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(21895, '16111', '01520987', '2011/11/13', '2011/12/04', 374230, 374230, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(28105, '16111', '01523518', '2011/11/17', '2011/12/08', 291330, 291330, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(28106, '16111', '01524666', '2011/11/20', '2011/12/11', 219210, 219210, 0, 'NF', 'KXH4906', 'T', null, '029') -receber values(32160, '16111', '01527206', '2011/11/24', '2011/12/15', 308130, 308130, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(33199, '16111', '01528343', '2011/11/27', '2011/12/18', 236490, 236490, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(16298, '16111', '02514279', '2011/10/31', '2011/11/21', 277320, 277320, 0, 'NF', 'C005', 'B', null, '029') -receber values(6473, '16137', '01505892', '2011/10/16', '2011/10/30', 710360, 713910, 3550, 'NF', 'KYK5632', 'B', null, '029') -receber values(6474, '16137', '01507609', '2011/10/19', '2011/11/02', 224700, 225820, 1120, 'NF', 'KYK5632', 'B', null, '029') -receber values(6475, '16137', '01508174', '2011/10/20', '2011/11/03', 582270, 582270, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(13726, '16137', '01511954', '2011/10/27', '2011/11/10', 738440, 738440, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16299, '16137', '01513365', '2011/10/31', '2011/11/14', 274650, 277400, 2750, 'NF', 'KYK5632', 'B', null, '029') -receber values(21896, '16137', '01515527', '2011/11/03', '2011/11/17', 504290, 504290, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21897, '16137', '01516547', '2011/11/04', '2011/11/18', 163450, 163450, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21898, '16137', '01519751', '2011/11/10', '2011/11/24', 1211770, 1211770, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21899, '16137', '01520936', '2011/11/13', '2011/11/27', 247500, 247500, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28107, '16137', '01523420', '2011/11/17', '2011/12/01', 896400, 896400, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28108, '16137', '01524277', '2011/11/18', '2011/12/02', 326960, 326960, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28109, '16137', '01524629', '2011/11/20', '2011/12/04', 365200, 365200, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28110, '16137', '01525886', '2011/11/22', '2011/12/06', 561220, 561220, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(32161, '16137', '01527161', '2011/11/24', '2011/12/08', 1427140, 1427140, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(33200, '16137', '01528259', '2011/11/27', '2011/12/11', 242460, 242460, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(35869, '16137', '01530115', '2011/11/30', '2011/12/14', 322500, 322500, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1456, '16181', '05244715', '2011/10/13', '2011/10/20', 324960, 347710, 22750, 'NF', 'KYK5632', 'B', null, '029') -receber values(6476, '16181', '05245441', '2011/10/18', '2011/10/25', 251660, 262980, 11320, 'NF', 'KYK5632', 'B', null, '029') -receber values(11355, '16181', '05246739', '2011/10/25', '2011/11/01', 309060, 321420, 12360, 'NF', 'KYK5632', 'B', null, '029') -receber values(16300, '16181', '05248103', '2011/11/01', '2011/11/08', 258070, 259360, 1290, 'NF', 'KYK5632', 'B', null, '029') -receber values(18899, '16181', '05249329', '2011/11/08', '2011/11/15', 266410, 275730, 9320, 'NF', 'KYK5632', 'B', null, '029') -receber values(28111, '16181', '05250893', '2011/11/16', '2011/11/23', 290050, 290050, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28112, '16181', '05251982', '2011/11/22', '2011/11/29', 257440, 257440, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34949, '16181', '05253239', '2011/11/29', '2011/12/06', 273010, 273010, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(35870, '18359', '01530112', '2011/11/30', '2011/12/10', 542830, 542830, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(21900, '18506', '01521641', '2011/11/14', '2011/12/05', 280040, 280040, 0, 'NF', 'LRX2193', 'T', null, '029') -receber values(28113, '18506', '01523567', '2011/11/17', '2011/12/08', 319200, 319200, 0, 'NF', 'KMN4639', 'T', null, '029') -receber values(28114, '18506', '01524362', '2011/11/18', '2011/12/09', 552620, 552620, 0, 'NF', 'KMN4639', 'T', null, '029') -receber values(6477, '18744', '01509113', '2011/10/21', '2011/11/11', 432700, 432700, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21901, '18744', '01521221', '2011/11/13', '2011/11/28', 405050, 405050, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(34018, '18744', '01529178', '2011/11/28', '2011/12/19', 1271730, 1271730, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(6478, '18935', '01507421', '2011/10/18', '2011/11/01', 1960220, 1960220, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(21902, '18935', '01517976', '2011/11/07', '2011/11/21', 610780, 610780, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(21903, '18935', '01520401', '2011/11/10', '2011/11/24', 602370, 602370, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(6479, '19297', '01505390', '2011/10/13', '2011/10/27', 139510, 139510, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(11356, '19297', '01510422', '2011/10/24', '2011/11/07', 132640, 132640, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(28115, '19297', '01524023', '2011/11/17', '2011/12/01', 201330, 201330, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(34950, '19297', '01529978', '2011/11/29', '2011/12/13', 177120, 177120, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(6480, '19913', '01502732', '2011/10/10', '2011/10/25', 399170, 399170, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6481, '19913', '01506272', '2011/10/17', '2011/11/01', 318920, 318920, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6482, '19913', '01508920', '2011/10/21', '2011/11/05', 232770, 232770, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(11357, '19913', '01509879', '2011/10/24', '2011/11/08', 215050, 215050, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(13727, '19913', '01511955', '2011/10/27', '2011/11/11', 151750, 151750, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16301, '19913', '01513364', '2011/10/31', '2011/11/15', 467770, 470110, 2340, 'NF', 'KYK5632', 'B', null, '029') -receber values(21904, '19913', '01515526', '2011/11/03', '2011/11/18', 331620, 331620, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21905, '19913', '01519017', '2011/11/09', '2011/11/24', 427560, 427560, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21906, '19913', '01521571', '2011/11/14', '2011/11/29', 525770, 525770, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28116, '19913', '01525130', '2011/11/21', '2011/12/06', 400750, 400750, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1457, '19998', '01498725', '2011/10/03', '2011/10/18', 408320, 420570, 12250, 'NF', 'KYK5632', 'B', null, '029') -receber values(6483, '19998', '01502741', '2011/10/10', '2011/10/25', 466020, 466020, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6484, '19998', '01506287', '2011/10/17', '2011/11/01', 454470, 459010, 4540, 'NF', 'KYK5632', 'B', null, '029') -receber values(6485, '19998', '01508922', '2011/10/21', '2011/11/05', 203170, 207230, 4060, 'NF', 'KYK5632', 'B', null, '029') -receber values(11358, '19998', '01509892', '2011/10/24', '2011/11/08', 264160, 264160, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(13728, '19998', '01511965', '2011/10/27', '2011/11/11', 221040, 221040, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16302, '19998', '01513382', '2011/10/31', '2011/11/15', 356660, 358440, 1780, 'NF', 'KYK5632', 'B', null, '029') -receber values(21907, '19998', '01515537', '2011/11/03', '2011/11/18', 315740, 315740, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21908, '19998', '01517457', '2011/11/07', '2011/11/22', 307090, 308630, 1540, 'NF', 'KYK5632', 'T', null, '029') -receber values(21909, '19998', '01519767', '2011/11/10', '2011/11/25', 344800, 344800, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(21910, '19998', '01521579', '2011/11/14', '2011/11/29', 431330, 431330, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28117, '19998', '01525142', '2011/11/21', '2011/12/06', 317220, 317220, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(32162, '19998', '01527174', '2011/11/24', '2011/12/09', 156050, 156050, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34951, '19998', '01529483', '2011/11/29', '2011/12/14', 302820, 302820, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1458, '20612', '01495148', '2011/09/26', '2011/10/17', 1116580, 1155660, 39080, 'NF', 'KYK5632', 'B', null, '029') -receber values(1459, '20612', '01498198', '2011/10/02', '2011/10/23', 652800, 656060, 3260, 'NF', 'KYK5632', 'B', null, '029') -receber values(6486, '20612', '01499464', '2011/10/04', '2011/10/24', 1194600, 1194600, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6487, '20612', '01499465', '2011/10/04', '2011/10/25', 1336600, 1376700, 40100, 'NF', 'KYK5632', 'B', null, '029') -receber values(6488, '20612', '01500924', '2011/10/06', '2011/10/27', 1131750, 1131750, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6489, '20612', '01501788', '2011/10/07', '2011/10/28', 1784120, 1837640, 53520, 'NF', 'KYK5632', 'B', null, '029') -receber values(6490, '20612', '01503627', '2011/10/11', '2011/11/01', 1048000, 1048000, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6491, '20612', '01504716', '2011/10/13', '2011/11/03', 461040, 461040, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6492, '20612', '01506286', '2011/10/17', '2011/11/07', 907400, 916470, 9070, 'NF', 'KYK5632', 'B', null, '029') -receber values(6493, '20612', '01508925', '2011/10/21', '2011/11/11', 390000, 399750, 9750, 'NF', 'KYK5632', 'B', null, '029') -receber values(11359, '20612', '01510634', '2011/10/25', '2011/11/15', 719600, 744790, 25190, 'NF', 'KYK5632', 'B', null, '029') -receber values(12851, '20612', '01511373', '2011/10/26', '2011/11/16', 77000, 79310, 2310, 'NF', 'KYK5632', 'B', null, '029') -receber values(16303, '20612', '01513379', '2011/10/31', '2011/11/21', 1514290, 1514290, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21911, '20612', '01518266', '2011/11/08', '2011/11/29', 269200, 269200, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28118, '20612', '01522699', '2011/11/16', '2011/12/07', 956840, 956840, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28119, '20612', '01525891', '2011/11/22', '2011/12/13', 1324050, 1324050, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34019, '20612', '01528775', '2011/11/28', '2011/12/19', 354020, 354020, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(6494, '20614', '01499342', '2011/10/04', '2011/10/24', 1134600, 1134600, 0, 'NF', 'KNH3108', 'B', null, '029') -receber values(6495, '20614', '01499343', '2011/10/04', '2011/10/25', 3234300, 3379840, 145540, 'NF', 'KNH3108', 'B', null, '029') -receber values(6496, '20614', '01501719', '2011/10/07', '2011/10/28', 338560, 348720, 10160, 'NF', 'KMM5715', 'B', null, '029') -receber values(6497, '20614', '01503526', '2011/10/11', '2011/10/31', 1281600, 1281600, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6498, '20614', '01503527', '2011/10/11', '2011/11/01', 2676770, 2676770, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6499, '20614', '01504692', '2011/10/13', '2011/11/03', 70720, 70720, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(6500, '20614', '01506163', '2011/10/17', '2011/11/07', 2954970, 2984520, 29550, 'NF', 'KNH3108', 'B', null, '029') -receber values(6501, '20614', '01506164', '2011/10/17', '2011/11/06', 622200, 622200, 0, 'NF', 'KNH3108', 'B', null, '029') -receber values(6502, '20614', '01508159', '2011/10/20', '2011/11/10', 764400, 787330, 22930, 'NF', 'KMM5715', 'B', null, '029') -receber values(11360, '20614', '01510603', '2011/10/25', '2011/11/15', 2145200, 2220280, 75080, 'NF', 'LSL2615', 'B', null, '029') -receber values(12852, '20614', '01511238', '2011/10/26', '2011/11/16', 1054000, 1085620, 31620, 'NF', 'KNH3108', 'B', null, '029') -receber values(16304, '20614', '01513337', '2011/10/31', '2011/11/21', 2246040, 2246040, 0, 'NF', 'KMM5715', 'B', null, '029') -receber values(21912, '20614', '01515471', '2011/11/03', '2011/11/24', 571200, 571200, 0, 'NF', 'KMM5715', 'B', null, '029') -receber values(21913, '20614', '01516479', '2011/11/04', '2011/11/25', 816000, 816000, 0, 'NF', 'KMM5715', 'B', null, '029') -receber values(21914, '20614', '01518183', '2011/11/08', '2011/11/29', 644400, 644400, 0, 'NF', 'KMM5715', 'B', null, '029') -receber values(21915, '20614', '01520533', '2011/11/11', '2011/12/02', 1239700, 1239700, 0, 'NF', 'KMM5715', 'T', null, '029') -receber values(28120, '20614', '01522596', '2011/11/16', '2011/12/07', 1602000, 1602000, 0, 'NF', 'KMM5715', 'T', null, '029') -receber values(28121, '20614', '01523386', '2011/11/17', '2011/12/08', 915960, 915960, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(28122, '20614', '01525850', '2011/11/22', '2011/12/13', 5227380, 5227380, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(34020, '20614', '01528682', '2011/11/28', '2011/12/19', 2420300, 2420300, 0, 'NF', 'KMM5715', 'T', null, '029') -receber values(16305, '21049', '01514398', '2011/10/31', '2011/11/07', 423880, 423880, 0, 'NF', 'LJN6251', 'B', null, '029') -receber values(28123, '21049', '01524501', '2011/11/18', '2011/11/25', 462140, 462140, 0, 'NF', 'LSX0479', 'B', null, '029') -receber values(34021, '21049', '01529272', '2011/11/28', '2011/12/05', 405420, 405420, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(6503, '21098', '01503264', '2011/10/10', '2011/10/24', 258750, 258750, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6504, '21098', '01507329', '2011/10/18', '2011/11/01', 541630, 541630, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(11361, '21098', '01510981', '2011/10/25', '2011/11/08', 273350, 273350, 0, 'NF', 'KUU6256', 'B', null, '029') -receber values(21916, '21098', '01516116', '2011/11/03', '2011/11/17', 3194270, 3194270, 0, 'NF', 'LJN6251', 'B', null, '029') -receber values(28124, '21098', '01523138', '2011/11/16', '2011/11/30', 311780, 311780, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(16306, '21098', '02514228', '2011/10/31', '2011/11/14', 190800, 192710, 1910, 'NF', 'DLU0835', 'B', null, '029') -receber values(6505, '22443', '01501483', '2011/10/06', '2011/10/27', 734270, 734270, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6506, '22443', '01506626', '2011/10/17', '2011/11/07', 515950, 515950, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6507, '22443', '01509487', '2011/10/23', '2011/11/13', 684500, 684500, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(13729, '22443', '01512462', '2011/10/27', '2011/11/17', 2981410, 2981410, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(13730, '22443', '01512463', '2011/10/27', '2011/11/17', 538970, 538970, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21917, '22443', '01516123', '2011/11/03', '2011/11/24', 408500, 408500, 0, 'NF', 'LJN6251', 'B', null, '029') -receber values(21918, '22443', '01520746', '2011/11/11', '2011/12/02', 761220, 761220, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(28125, '22443', '01524783', '2011/11/20', '2011/12/11', 572290, 572290, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(28126, '22443', '01524784', '2011/11/20', '2011/12/11', 670540, 670540, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(32163, '22443', '01527704', '2011/11/24', '2011/12/15', 935780, 935780, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(34022, '22443', '01529182', '2011/11/28', '2011/12/19', 437130, 437130, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(34952, '22443', '01529891', '2011/11/29', '2011/12/20', 497200, 497200, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(6508, '22820', '01502737', '2011/10/10', '2011/10/25', 412430, 412430, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6509, '22820', '01506886', '2011/10/18', '2011/11/02', 348850, 350590, 1740, 'NF', 'KYK5632', 'B', null, '029') -receber values(11362, '22820', '01509893', '2011/10/24', '2011/11/08', 484170, 484170, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16307, '22820', '01513378', '2011/10/31', '2011/11/15', 304280, 305800, 1520, 'NF', 'KYK5632', 'B', null, '029') -receber values(21919, '22820', '01517451', '2011/11/07', '2011/11/22', 158750, 158750, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21920, '22820', '01519022', '2011/11/09', '2011/11/24', 188240, 188240, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28127, '22820', '01523426', '2011/11/17', '2011/12/02', 275990, 275990, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1460, '23624', '05244471', '2011/10/12', '2011/10/22', 163430, 170780, 7350, 'NF', 'KYK5632', 'B', null, '029') -receber values(21921, '23624', '05249328', '2011/11/08', '2011/11/18', 251670, 256700, 5030, 'NF', 'KYK5632', 'T', null, '029') -receber values(6510, '24105', '01505924', '2011/10/16', '2011/11/06', 327050, 327050, 0, 'NF', 'KRB6158', 'B', null, '029') -receber values(11363, '24105', '01510058', '2011/10/24', '2011/11/14', 231150, 233460, 2310, 'NF', 'KMN4639', 'B', null, '029') -receber values(21922, '24105', '01521657', '2011/11/14', '2011/12/05', 317000, 317000, 0, 'NF', 'LRX2193', 'T', null, '029') -receber values(28128, '24105', '01524355', '2011/11/18', '2011/12/09', 307350, 307350, 0, 'NF', 'KXH2344', 'T', null, '029') -receber values(28129, '24105', '01525256', '2011/11/21', '2011/12/12', 80750, 80750, 0, 'NF', 'KMN4639', 'T', null, '029') -receber values(31248, '24105', '01526655', '2011/11/23', '2011/12/14', 132140, 132140, 0, 'NF', 'KXH2344', 'T', null, '029') -receber values(1461, '24156', '01413896', '2011/04/25', '2011/05/05', 482600, 936240, 453640, 'NF', 'KYK5632', 'T', null, '029') -receber values(1462, '24156', '01416639', '2011/04/29', '2011/05/09', 286250, 549600, 263350, 'NF', 'KYK5632', 'T', null, '029') -receber values(6511, '24726', '01503262', '2011/10/10', '2011/10/24', 187330, 187330, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6512, '24726', '01506619', '2011/10/17', '2011/10/31', 57500, 57500, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(16308, '24726', '01514685', '2011/11/01', '2011/11/15', 256680, 257960, 1280, 'NF', 'DLU0835', 'B', null, '029') -receber values(21923, '24726', '01520326', '2011/11/10', '2011/11/24', 190030, 190030, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(28130, '24726', '01524069', '2011/11/17', '2011/12/01', 150280, 150280, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(28131, '24726', '01526257', '2011/11/22', '2011/12/06', 671220, 671220, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(31249, '24726', '01526980', '2011/11/23', '2011/12/07', 75040, 75040, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(34953, '24726', '01529885', '2011/11/29', '2011/12/13', 238800, 238800, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(16309, '24726', '02514227', '2011/10/31', '2011/11/28', 241200, 241200, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(6513, '25320', '01499492', '2011/10/04', '2011/10/25', 3785100, 3955430, 170330, 'NF', 'LBT3689', 'B', null, '029') -receber values(1463, '25320', '01499493', '2011/10/04', '2011/10/22', 1791600, 1809520, 17920, 'NF', 'LBT3689', 'B', null, '029') -receber values(1464, '25320', '01499494', '2011/10/04', '2011/10/22', 1116000, 1127160, 11160, 'NF', 'LBT3689', 'B', null, '029') -receber values(6514, '25320', '01499495', '2011/10/04', '2011/10/24', 2063840, 2063840, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6515, '25320', '01499496', '2011/10/04', '2011/10/25', 1826290, 1881080, 54790, 'NF', 'LBT3689', 'B', null, '029') -receber values(6516, '25320', '01500278', '2011/10/05', '2011/10/26', 1032000, 1032000, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6517, '25320', '01500949', '2011/10/06', '2011/10/27', 334340, 334340, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6518, '25320', '01501734', '2011/10/07', '2011/10/26', 3603600, 3603600, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6519, '25320', '01501735', '2011/10/07', '2011/10/28', 3680820, 3901670, 220850, 'NF', 'LBT3689', 'B', null, '029') -receber values(6520, '25320', '01503655', '2011/10/11', '2011/10/30', 1133520, 1139190, 5670, 'NF', 'LBT3689', 'B', null, '029') -receber values(6521, '25320', '01503656', '2011/10/11', '2011/10/31', 1202430, 1202430, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6522, '25320', '01503657', '2011/10/11', '2011/11/01', 2320660, 2320660, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6523, '25320', '01505588', '2011/10/14', '2011/11/04', 336600, 336600, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6524, '25320', '01506316', '2011/10/17', '2011/11/05', 1481640, 1511270, 29630, 'NF', 'LBT3689', 'B', null, '029') -receber values(6525, '25320', '01506317', '2011/10/17', '2011/11/06', 1980000, 2009700, 29700, 'NF', 'LBT3689', 'B', null, '029') -receber values(6526, '25320', '01506318', '2011/10/17', '2011/11/07', 2524760, 2550010, 25250, 'NF', 'LBT3689', 'B', null, '029') -receber values(6527, '25320', '01507594', '2011/10/19', '2011/11/09', 5000300, 5025300, 25000, 'NF', 'LBT3689', 'B', null, '029') -receber values(11364, '25320', '01510689', '2011/10/25', '2011/11/15', 4297920, 4319410, 21490, 'NF', 'LBT3689', 'B', null, '029') -receber values(12853, '25320', '01511392', '2011/10/26', '2011/11/16', 696000, 716880, 20880, 'NF', 'LBT3689', 'B', null, '029') -receber values(12854, '25320', '01511393', '2011/10/26', '2011/11/16', 4518310, 4653860, 135550, 'NF', 'LBT3689', 'B', null, '029') -receber values(16310, '25320', '01513615', '2011/10/31', '2011/11/20', 3882040, 3882040, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(16311, '25320', '01513616', '2011/10/31', '2011/11/21', 2347400, 2347400, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(16312, '25320', '01513617', '2011/10/31', '2011/11/21', 2543400, 2543400, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(21924, '25320', '01515577', '2011/11/03', '2011/11/24', 1611840, 1611840, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21925, '25320', '01516587', '2011/11/04', '2011/11/25', 308760, 308760, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21926, '25320', '01518279', '2011/11/08', '2011/11/27', 1683720, 1683720, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21927, '25320', '01518280', '2011/11/08', '2011/11/28', 2299500, 2299500, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21928, '25320', '01518281', '2011/11/08', '2011/11/29', 2453500, 2453500, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(28132, '25320', '01522727', '2011/11/16', '2011/12/07', 3204230, 3204230, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(28133, '25320', '01523519', '2011/11/17', '2011/12/08', 450000, 450000, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(28134, '25320', '01525971', '2011/11/22', '2011/12/10', 4286950, 4286950, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(28135, '25320', '01525982', '2011/11/22', '2011/12/13', 3419560, 3419560, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(33201, '25320', '01527953', '2011/11/25', '2011/12/16', 888000, 888000, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(34023, '25320', '01528806', '2011/11/28', '2011/12/16', 2196600, 2196600, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(34024, '25320', '01528807', '2011/11/28', '2011/12/19', 2120750, 2120750, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(16313, '25320', '02514255', '2011/10/31', '2011/11/21', 3882040, 3882040, 0, 'NF', 'C005', 'B', null, '029') -receber values(16314, '25320', '02514256', '2011/10/31', '2011/11/21', 1651400, 1651400, 0, 'NF', 'C005', 'B', null, '029') -receber values(16315, '25320', '02514257', '2011/10/31', '2011/11/21', 2543400, 2543400, 0, 'NF', 'C005', 'B', null, '029') -receber values(1465, '25625', '01495147', '2011/09/26', '2011/10/10', 278040, 300280, 22240, 'NF', 'KYK5632', 'B', null, '029') -receber values(1466, '25625', '01498726', '2011/10/03', '2011/10/17', 482040, 508550, 26510, 'NF', 'KYK5632', 'B', null, '029') -receber values(6528, '25625', '01506282', '2011/10/17', '2011/10/31', 458860, 465740, 6880, 'NF', 'KYK5632', 'B', null, '029') -receber values(11365, '25625', '01509894', '2011/10/24', '2011/11/07', 264160, 266800, 2640, 'NF', 'KYK5632', 'B', null, '029') -receber values(16316, '25625', '01513377', '2011/10/31', '2011/11/14', 386140, 390000, 3860, 'NF', 'KYK5632', 'B', null, '029') -receber values(21929, '25625', '01517449', '2011/11/07', '2011/11/21', 562540, 565350, 2810, 'NF', 'KYK5632', 'T', null, '029') -receber values(21930, '25625', '01521577', '2011/11/14', '2011/11/28', 216840, 216840, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28136, '25625', '01525141', '2011/11/21', '2011/12/05', 215300, 215300, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34954, '25625', '01529479', '2011/11/29', '2011/12/13', 222840, 222840, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1467, '26071', '01504019', '2011/10/12', '2011/10/22', 324910, 328160, 3250, 'NF', 'LSL2615', 'B', null, '029') -receber values(1468, '26071', '01504736', '2011/10/13', '2011/10/23', 463430, 465750, 2320, 'NF', 'KMM5715', 'B', null, '029') -receber values(6529, '26071', '01508145', '2011/10/20', '2011/10/30', 564350, 567170, 2820, 'NF', 'KMM5715', 'B', null, '029') -receber values(13731, '26071', '01511937', '2011/10/27', '2011/11/18', 223570, 223570, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(14758, '26071', '01512992', '2011/10/30', '2011/11/09', 107950, 107950, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(21931, '26071', '01519736', '2011/11/10', '2011/11/20', 234130, 234130, 0, 'NF', 'LSL2615', 'B', null, '029') -receber values(28137, '26071', '01523331', '2011/11/17', '2011/11/27', 418650, 420740, 2090, 'NF', 'KMM5715', 'B', null, '029') -receber values(28138, '26071', '01525735', '2011/11/22', '2011/12/02', 114200, 114200, 0, 'NF', 'KMM5715', 'T', null, '029') -receber values(32164, '26071', '01527260', '2011/11/24', '2011/12/04', 513600, 513600, 0, 'NF', 'LSL2615', 'T', null, '029') -receber values(21932, '28239', '01517447', '2011/11/07', '2011/11/28', 428370, 428370, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(32165, '28239', '01527175', '2011/11/24', '2011/12/15', 406370, 406370, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34025, '28239', '01528774', '2011/11/28', '2011/12/19', 952380, 952380, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34026, '28431', '01528754', '2011/11/28', '2011/12/12', 378430, 378430, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(33202, '28745', '01528255', '2011/11/27', '2011/12/18', 947360, 947360, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1469, '28798', '01495570', '2011/09/26', '2011/10/09', 722360, 783760, 61400, 'NF', 'KXH2344', 'B', null, '029') -receber values(1470, '28798', '01495571', '2011/09/26', '2011/10/10', 736310, 795210, 58900, 'NF', 'KXH2344', 'B', null, '029') -receber values(1471, '28798', '01496327', '2011/09/27', '2011/10/11', 607700, 677590, 69890, 'NF', 'LCP3836', 'B', null, '029') -receber values(1472, '28798', '01497744', '2011/09/29', '2011/10/12', 566050, 628320, 62270, 'NF', 'LCP3836', 'B', null, '029') -receber values(1473, '28798', '01497746', '2011/09/29', '2011/10/13', 560100, 618910, 58810, 'NF', 'LCP3836', 'B', null, '029') -receber values(1474, '28798', '01498426', '2011/10/02', '2011/10/14', 524400, 592570, 68170, 'NF', 'LCE4302', 'B', null, '029') -receber values(1475, '28798', '01498427', '2011/10/02', '2011/10/16', 620800, 695300, 74500, 'NF', 'LCE4302', 'B', null, '029') -receber values(1476, '28798', '01499995', '2011/10/04', '2011/10/17', 874520, 975090, 100570, 'NF', 'LCP3836', 'B', null, '029') -receber values(1477, '28798', '01499996', '2011/10/04', '2011/10/18', 792620, 879810, 87190, 'NF', 'LCP3836', 'B', null, '029') -receber values(1478, '28798', '01503435', '2011/10/10', '2011/10/22', 564000, 614760, 50760, 'NF', 'LGY8427', 'B', null, '029') -receber values(6530, '28798', '01508013', '2011/10/19', '2011/11/01', 650600, 676620, 26020, 'NF', 'LCP3836', 'B', null, '029') -receber values(6531, '28798', '01508014', '2011/10/19', '2011/11/02', 727850, 753320, 25470, 'NF', 'LCP3836', 'B', null, '029') -receber values(28139, '28798', '01525658', '2011/11/21', '2011/12/04', 954110, 954110, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(28140, '28798', '01525659', '2011/11/21', '2011/12/05', 912830, 912830, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(32166, '28798', '01527788', '2011/11/24', '2011/12/07', 734160, 734160, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(32167, '28798', '01527789', '2011/11/24', '2011/12/08', 958400, 958400, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(34027, '28798', '01529240', '2011/11/28', '2011/12/12', 1013070, 1013070, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(1479, '28904', '05242768', '2011/10/03', '2011/10/18', 499920, 514920, 15000, 'NF', 'DLU0835', 'B', null, '029') -receber values(1480, '28904', '05243300', '2011/10/05', '2011/10/20', 387920, 395680, 7760, 'NF', 'DLU0835', 'B', null, '029') -receber values(1481, '28904', '05243786', '2011/10/07', '2011/10/22', 559610, 593190, 33580, 'NF', 'DLU0835', 'B', null, '029') -receber values(6532, '28904', '05244637', '2011/10/12', '2011/10/27', 310430, 321300, 10870, 'NF', 'DLU0835', 'B', null, '029') -receber values(6533, '28904', '05245114', '2011/10/14', '2011/10/29', 718830, 736800, 17970, 'NF', 'DLU0835', 'B', null, '029') -receber values(6534, '28904', '05246162', '2011/10/20', '2011/11/04', 664310, 680920, 16610, 'NF', 'DLU0835', 'B', null, '029') -receber values(11366, '28904', '05246653', '2011/10/24', '2011/11/08', 671540, 674900, 3360, 'NF', 'DLU0835', 'B', null, '029') -receber values(12855, '28904', '05247174', '2011/10/26', '2011/11/10', 824380, 873840, 49460, 'NF', 'DLU0835', 'B', null, '029') -receber values(16317, '28904', '05248002', '2011/10/31', '2011/11/15', 725250, 750630, 25380, 'NF', 'DLU0835', 'B', null, '029') -receber values(21933, '28904', '05249497', '2011/11/08', '2011/11/23', 647870, 647870, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(21934, '28904', '05250272', '2011/11/11', '2011/11/26', 803190, 803190, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(28141, '28904', '05251092', '2011/11/16', '2011/12/01', 630690, 630690, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(31250, '28904', '05252456', '2011/11/23', '2011/12/08', 935610, 935610, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(34955, '28904', '05253402', '2011/11/29', '2011/12/14', 978950, 978950, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(6535, '29116', '01509510', '2011/10/23', '2011/11/02', 206450, 207480, 1030, 'NF', 'LUF0315', 'B', null, '029') -receber values(35871, '29116', '01530541', '2011/11/30', '2011/12/10', 135800, 135800, 0, 'NF', 'LUF0315', 'T', null, '029') -receber values(6536, '29237', '01508178', '2011/10/20', '2011/10/30', 191460, 192420, 960, 'NF', 'KYK5632', 'B', null, '029') -receber values(16318, '29237', '01513371', '2011/10/31', '2011/11/10', 264170, 264170, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21935, '29237', '01519758', '2011/11/10', '2011/11/20', 230070, 230070, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(32168, '29237', '01527170', '2011/11/24', '2011/12/04', 235710, 235710, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(6537, '29515', '01499980', '2011/10/04', '2011/10/25', 902250, 902250, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(6538, '29515', '01500710', '2011/10/05', '2011/10/26', 412400, 412400, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(6539, '29515', '01501986', '2011/10/07', '2011/10/28', 415000, 415000, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(6540, '29515', '01505750', '2011/10/14', '2011/11/04', 680800, 680800, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(6541, '29515', '01508828', '2011/10/20', '2011/11/10', 833600, 833600, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(16319, '29515', '01514660', '2011/11/01', '2011/11/22', 1203380, 1203380, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(21936, '29515', '01517983', '2011/11/07', '2011/11/28', 397260, 397260, 0, 'NF', 'LCP3836', 'B', null, '029') -receber values(21937, '29515', '01520409', '2011/11/10', '2011/12/01', 608790, 608790, 0, 'NF', 'LGY8427', 'T', null, '029') -receber values(21938, '29515', '01521788', '2011/11/14', '2011/12/05', 1389010, 1389010, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(28142, '29515', '01524470', '2011/11/18', '2011/12/09', 549800, 549800, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(28143, '29515', '01525648', '2011/11/21', '2011/12/12', 1315720, 1315720, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(34028, '29515', '01529225', '2011/11/28', '2011/12/19', 871150, 871150, 0, 'NF', 'LCP3836', 'T', null, '029') -receber values(16320, '29515', '02514373', '2011/10/31', '2011/11/21', 1161800, 1161800, 0, 'NF', 'LGY8427', 'B', null, '029') -receber values(1482, '29516', '01207466', '2011/02/19', '2011/03/05', 338000, 1375660, 1037660, 'NF', 'LRX2193', 'T', null, '029') -receber values(6542, '29607', '01502111', '2011/10/09', '2011/10/30', 235700, 235700, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6543, '29607', '01504700', '2011/10/13', '2011/11/03', 522610, 522610, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6544, '29607', '01506876', '2011/10/18', '2011/11/08', 165800, 165800, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6545, '29607', '01508172', '2011/10/20', '2011/11/10', 284230, 284230, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16321, '29607', '01513361', '2011/10/31', '2011/11/21', 481840, 481840, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21939, '29607', '01515521', '2011/11/03', '2011/11/24', 350430, 350430, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21940, '29607', '01516930', '2011/11/06', '2011/11/27', 185040, 185040, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21941, '29607', '01519015', '2011/11/09', '2011/11/30', 461450, 461450, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28144, '29607', '01522683', '2011/11/16', '2011/12/07', 170290, 170290, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28145, '29607', '01525126', '2011/11/21', '2011/12/12', 276200, 276200, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34956, '29607', '01529474', '2011/11/29', '2011/12/20', 329930, 329930, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34957, '29607', '01529475', '2011/11/29', '2011/12/20', 424960, 424960, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1483, '29744', '01486687', '2011/09/09', '2011/09/16', 769970, 1062560, 292590, 'NF', 'LCP3836', 'T', null, '029') -receber values(13732, '29878', '01511962', '2011/10/27', '2011/11/17', 172440, 172440, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21942, '29878', '01517443', '2011/11/07', '2011/11/28', 434350, 434350, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(32169, '29878', '01527165', '2011/11/24', '2011/12/15', 511290, 511290, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34029, '29878', '01528771', '2011/11/28', '2011/12/19', 1684950, 1684950, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(11367, '29879', '01509738', '2011/10/24', '2011/11/14', 425630, 425630, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21943, '29879', '01518876', '2011/11/09', '2011/11/30', 357760, 357760, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6546, '29922', '01501513', '2011/10/06', '2011/10/27', 973800, 973800, 0, 'NF', 'LCE4302', 'B', null, '029') -receber values(6547, '29922', '01507317', '2011/10/18', '2011/11/08', 1684970, 1684970, 0, 'NF', 'LCE4302', 'B', null, '029') -receber values(32170, '29922', '01527745', '2011/11/24', '2011/12/15', 1449700, 1449700, 0, 'NF', 'LCE4302', 'T', null, '029') -receber values(34958, '29922', '01529936', '2011/11/29', '2011/12/20', 1283550, 1283550, 0, 'NF', 'LCE4302', 'T', null, '029') -receber values(16322, '29922', '02514315', '2011/10/31', '2011/11/21', 537920, 537920, 0, 'NF', 'LCE4302', 'B', null, '029') -receber values(1484, '30159', '01498202', '2011/10/02', '2011/10/23', 132980, 133640, 660, 'NF', 'KYK5632', 'B', null, '029') -receber values(6548, '30159', '01498721', '2011/10/03', '2011/10/24', 177480, 177480, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6549, '30159', '01500249', '2011/10/05', '2011/10/26', 93500, 93500, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6550, '30159', '01502744', '2011/10/10', '2011/10/31', 245610, 245610, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6551, '30159', '01506279', '2011/10/17', '2011/11/07', 174170, 174170, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6552, '30159', '01509267', '2011/10/23', '2011/11/13', 354880, 360200, 5320, 'NF', 'CXV9668', 'B', null, '029') -receber values(11368, '30159', '01509885', '2011/10/24', '2011/11/14', 232740, 235070, 2330, 'NF', 'KYK5632', 'B', null, '029') -receber values(14759, '30159', '01512997', '2011/10/30', '2011/11/20', 365980, 365980, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21944, '30159', '01515529', '2011/11/03', '2011/11/24', 274340, 274340, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28146, '30159', '01523423', '2011/11/17', '2011/12/08', 233500, 233500, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28147, '30159', '01525133', '2011/11/21', '2011/12/12', 178420, 178420, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(33203, '30159', '01528258', '2011/11/27', '2011/12/18', 360060, 360060, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(16323, '31271', '01513385', '2011/10/31', '2011/11/14', 647880, 654360, 6480, 'NF', 'LQO1343', 'B', null, '029') -receber values(1485, '31292', '01503949', '2011/10/12', '2011/10/22', 435130, 439480, 4350, 'NF', 'KYK5632', 'B', null, '029') -receber values(6553, '31292', '01506280', '2011/10/17', '2011/10/27', 567780, 567780, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6554, '31292', '01507614', '2011/10/19', '2011/10/29', 124800, 124800, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6555, '31292', '01509270', '2011/10/23', '2011/11/02', 480070, 480070, 0, 'NF', 'CXV9668', 'B', null, '029') -receber values(11369, '31292', '01510631', '2011/10/25', '2011/11/04', 574460, 574460, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(14760, '31292', '01512701', '2011/10/28', '2011/11/07', 577360, 577360, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16324, '31292', '01513372', '2011/10/31', '2011/11/10', 311130, 311130, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16325, '31292', '01514920', '2011/11/02', '2011/11/12', 620840, 633260, 12420, 'NF', 'KYK5632', 'B', null, '029') -receber values(18900, '31292', '01515534', '2011/11/03', '2011/11/13', 704860, 715430, 10570, 'NF', 'KYK5632', 'B', null, '029') -receber values(18901, '31292', '01516540', '2011/11/04', '2011/11/14', 162480, 164100, 1620, 'NF', 'KYK5632', 'B', null, '029') -receber values(21945, '31292', '01518263', '2011/11/08', '2011/11/18', 490520, 490520, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28148, '31292', '01522693', '2011/11/16', '2011/11/26', 529760, 529760, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28149, '31292', '01522695', '2011/11/16', '2011/11/26', 487580, 487580, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28150, '31292', '01525136', '2011/11/21', '2011/12/01', 666770, 666770, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(32171, '31292', '01527171', '2011/11/24', '2011/12/04', 648630, 648630, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(33204, '31292', '01527929', '2011/11/25', '2011/12/05', 644540, 644540, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(35872, '31292', '01530109', '2011/11/30', '2011/12/10', 496290, 496290, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(14761, '31314', '01513206', '2011/10/30', '2011/11/09', 416600, 416600, 0, 'NF', 'KUV3781', 'B', null, '029') -receber values(6556, '31503', '01508740', '2011/10/20', '2011/11/04', 182520, 182520, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21946, '31503', '01517930', '2011/11/07', '2011/11/28', 845280, 845280, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6557, '31541', '01509298', '2011/10/23', '2011/11/02', 206450, 207480, 1030, 'NF', 'KXH4906', 'B', null, '029') -receber values(35873, '31541', '01530216', '2011/11/30', '2011/12/10', 135800, 135800, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(1486, '32020', '01497215', '2011/09/29', '2011/10/09', 1061310, 1178050, 116740, 'NF', 'CXV9668', 'B', null, '029') -receber values(1487, '32020', '01497216', '2011/09/29', '2011/10/08', 1049750, 1170470, 120720, 'NF', 'CXV9668', 'B', null, '029') -receber values(1488, '32020', '01501724', '2011/10/07', '2011/10/17', 814660, 883910, 69250, 'NF', 'LBT3689', 'B', null, '029') -receber values(1489, '32020', '01502139', '2011/10/09', '2011/10/18', 951170, 1027260, 76090, 'NF', 'LBT3689', 'B', null, '029') -receber values(1490, '32020', '01502140', '2011/10/09', '2011/10/19', 814150, 875210, 61060, 'NF', 'LBT3689', 'B', null, '029') -receber values(6558, '32020', '01505902', '2011/10/16', '2011/10/26', 1403070, 1501280, 98210, 'NF', 'KMI7446', 'B', null, '029') -receber values(6559, '32020', '01505903', '2011/10/16', '2011/10/25', 1137680, 1188880, 51200, 'NF', 'KMI7446', 'B', null, '029') -receber values(6560, '32020', '01508214', '2011/10/20', '2011/10/29', 754640, 796150, 41510, 'NF', 'CXV9668', 'B', null, '029') -receber values(6561, '32020', '01508215', '2011/10/20', '2011/10/29', 2332460, 2460750, 128290, 'NF', 'CXV9668', 'B', null, '029') -receber values(14762, '32020', '01513011', '2011/10/30', '2011/11/09', 1118740, 1124330, 5590, 'NF', 'LBT3689', 'B', null, '029') -receber values(14763, '32020', '01513012', '2011/10/30', '2011/11/09', 3230490, 3246640, 16150, 'NF', 'LBT3689', 'B', null, '029') -receber values(21947, '32020', '01516842', '2011/11/06', '2011/11/16', 3555890, 3662570, 106680, 'NF', 'LBT3689', 'T', null, '029') -receber values(21948, '32020', '01516843', '2011/11/06', '2011/11/16', 1218360, 1254910, 36550, 'NF', 'LBT3689', 'B', null, '029') -receber values(28151, '32020', '01522706', '2011/11/16', '2011/11/25', 815400, 815400, 0, 'NF', 'CXV9668', 'B', null, '029') -receber values(28152, '32020', '01522707', '2011/11/16', '2011/11/26', 1859080, 1859080, 0, 'NF', 'CXV9668', 'B', null, '029') -receber values(31251, '32020', '01526456', '2011/11/23', '2011/12/03', 2010700, 2010700, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(31252, '32020', '01526457', '2011/11/23', '2011/12/03', 532790, 532790, 0, 'NF', 'CXV9668', 'T', null, '029') -receber values(34959, '32020', '01529501', '2011/11/29', '2011/12/09', 4001380, 4001380, 0, 'NF', 'KCC1472', 'T', null, '029') -receber values(34960, '32020', '01529502', '2011/11/29', '2011/12/09', 868940, 868940, 0, 'NF', 'KCC1472', 'T', null, '029') -receber values(6562, '32313', '01505235', '2011/10/13', '2011/10/27', 527210, 527210, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6563, '32313', '01505716', '2011/10/14', '2011/10/28', 224800, 224800, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(6564, '32313', '01509112', '2011/10/21', '2011/11/04', 286260, 286260, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21949, '32313', '01517941', '2011/11/07', '2011/11/21', 205700, 205700, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(21950, '32313', '01519479', '2011/11/09', '2011/11/23', 352980, 352980, 0, 'NF', 'DLU0835', 'B', null, '029') -receber values(28153, '32313', '01526255', '2011/11/22', '2011/12/06', 389180, 389180, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(33205, '32313', '01528163', '2011/11/25', '2011/12/09', 234600, 234600, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(34961, '32313', '01529898', '2011/11/29', '2011/12/13', 143930, 143930, 0, 'NF', 'DLU0835', 'T', null, '029') -receber values(29, '32313', '850008', '2011/08/29', '2011/08/23', 506000, 698280, 192280, 'CHQ', 'INEXIST', 'T', null, '029') -receber values(1491, '32423', '01367141', '2011/01/19', '2011/01/24', 257050, 628490, 371440, 'NF', 'KYK5632', 'T', null, '029') -receber values(1492, '32423', '01369211', '2011/01/24', '2011/01/29', 264780, 640770, 375990, 'NF', 'KYK5632', 'T', null, '029') -receber values(1493, '32423', '01369212', '2011/01/24', '2011/01/29', 106100, 256760, 150660, 'NF', 'KYK5632', 'T', null, '029') -receber values(1494, '32423', '01370578', '2011/01/26', '2011/01/31', 300200, 723480, 423280, 'NF', 'KYK5632', 'T', null, '029') -receber values(16326, '32730', '01514724', '2011/11/01', '2011/11/22', 404720, 406740, 2020, 'NF', 'KMN4639', 'B', null, '029') -receber values(21951, '32730', '01518399', '2011/11/08', '2011/11/29', 168800, 168800, 0, 'NF', 'LUP0560', 'B', null, '029') -receber values(21952, '32730', '01521654', '2011/11/14', '2011/12/05', 279160, 279160, 0, 'NF', 'LRX2193', 'T', null, '029') -receber values(21953, '32730', '01521655', '2011/11/14', '2011/12/05', 372190, 372190, 0, 'NF', 'LRX2193', 'T', null, '029') -receber values(21954, '32730', '01522086', '2011/11/15', '2011/12/06', 462800, 462800, 0, 'NF', 'KRB6158', 'T', null, '029') -receber values(28154, '32730', '01523557', '2011/11/17', '2011/12/08', 232350, 232350, 0, 'NF', 'KXH2344', 'T', null, '029') -receber values(28155, '32730', '01525254', '2011/11/21', '2011/12/12', 662130, 662130, 0, 'NF', 'KMN4639', 'T', null, '029') -receber values(28156, '32730', '01525991', '2011/11/22', '2011/12/13', 83600, 83600, 0, 'NF', 'KXH2344', 'T', null, '029') -receber values(6565, '32749', '01497179', '2011/09/29', '2011/10/27', 1905320, 1905320, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6566, '32749', '01498722', '2011/10/03', '2011/10/31', 55800, 55800, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6567, '32749', '01500917', '2011/10/06', '2011/11/03', 1025160, 1025160, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6568, '32749', '01504709', '2011/10/13', '2011/11/10', 510600, 510600, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(13733, '32749', '01511961', '2011/10/27', '2011/11/24', 345300, 345300, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21955, '32749', '01515531', '2011/11/03', '2011/12/01', 1941200, 1941200, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(21956, '32749', '01516544', '2011/11/04', '2011/12/02', 74790, 74790, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(21957, '32749', '01517444', '2011/11/07', '2011/12/05', 283900, 283900, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(32172, '32749', '01527167', '2011/11/24', '2011/12/22', 579400, 579400, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(6569, '32751', '01496987', '2011/09/29', '2011/10/27', 807820, 807820, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6570, '32751', '01500801', '2011/10/06', '2011/11/03', 208390, 208390, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6571, '32751', '01504592', '2011/10/13', '2011/11/10', 558100, 558100, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(11370, '32751', '01509726', '2011/10/24', '2011/11/21', 1042730, 1042730, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(13734, '32751', '01511845', '2011/10/27', '2011/11/24', 240000, 240000, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(21958, '32751', '01515667', '2011/11/03', '2011/12/01', 1692710, 1692710, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(21959, '32751', '01516420', '2011/11/04', '2011/12/02', 246670, 246670, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(21960, '32751', '01517239', '2011/11/07', '2011/12/05', 142350, 142350, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(21961, '32751', '01519640', '2011/11/10', '2011/12/08', 143870, 143870, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(32173, '32751', '01527068', '2011/11/24', '2011/12/22', 459800, 459800, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(1495, '32836', '01502013', '2011/10/07', '2011/10/17', 290240, 310560, 20320, 'NF', 'LSX0479', 'B', null, '029') -receber values(1496, '32836', '01505362', '2011/10/13', '2011/10/23', 384800, 400190, 15390, 'NF', 'LSX0479', 'B', null, '029') -receber values(6572, '32836', '01509141', '2011/10/21', '2011/10/31', 351400, 367210, 15810, 'NF', 'LSX0479', 'B', null, '029') -receber values(13735, '32836', '01512590', '2011/10/27', '2011/11/06', 317600, 322360, 4760, 'NF', 'LSX0479', 'B', null, '029') -receber values(18902, '32836', '01516258', '2011/11/03', '2011/11/13', 376800, 393760, 16960, 'NF', 'LGY8427', 'B', null, '029') -receber values(21962, '32836', '01518828', '2011/11/08', '2011/11/18', 437090, 445830, 8740, 'NF', 'LSX0479', 'B', null, '029') -receber values(21963, '32836', '01520823', '2011/11/11', '2011/11/21', 439900, 442100, 2200, 'NF', 'LSX0479', 'T', null, '029') -receber values(28157, '32836', '01523198', '2011/11/16', '2011/11/26', 346400, 346400, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(28158, '32836', '01525690', '2011/11/21', '2011/12/01', 400400, 400400, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(33206, '32836', '01528149', '2011/11/25', '2011/12/05', 544470, 544470, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(34962, '32836', '01529983', '2011/11/29', '2011/12/09', 384800, 384800, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(1497, '33015', '01504710', '2011/10/13', '2011/10/23', 241750, 246590, 4840, 'NF', 'KYK5632', 'B', null, '029') -receber values(13736, '33015', '01511969', '2011/10/27', '2011/11/06', 502480, 565290, 62810, 'NF', 'KYK5632', 'T', null, '029') -receber values(21964, '33015', '01517452', '2011/11/07', '2011/11/17', 321280, 343770, 22490, 'NF', 'KYK5632', 'T', null, '029') -receber values(1498, '33159', '01494510', '2011/09/23', '2011/10/07', 308300, 349920, 41620, 'NF', 'LSX0479', 'B', null, '029') -receber values(1499, '33159', '01502008', '2011/10/07', '2011/10/21', 125600, 133760, 8160, 'NF', 'LSX0479', 'B', null, '029') -receber values(6573, '33159', '01507410', '2011/10/18', '2011/11/01', 216600, 225260, 8660, 'NF', 'LSX0479', 'T', null, '029') -receber values(6574, '33159', '01507983', '2011/10/19', '2011/11/02', 333100, 344760, 11660, 'NF', 'LSX0479', 'T', null, '029') -receber values(21965, '33159', '01516799', '2011/11/04', '2011/11/18', 291410, 297240, 5830, 'NF', 'LSX0479', 'T', null, '029') -receber values(21966, '33159', '01519540', '2011/11/09', '2011/11/23', 219620, 219620, 0, 'NF', 'LSX0479', 'T', null, '029') -receber values(6575, '33334', '01501745', '2011/10/07', '2011/10/28', 345920, 366680, 20760, 'NF', 'KYV1613', 'B', null, '029') -receber values(6576, '33334', '01503544', '2011/10/11', '2011/11/01', 1346380, 1346380, 0, 'NF', 'KMM5715', 'B', null, '029') -receber values(11371, '33335', '01509887', '2011/10/24', '2011/11/14', 651580, 677640, 26060, 'NF', 'KYK5632', 'B', null, '029') -receber values(11372, '33335', '01510629', '2011/10/25', '2011/11/15', 185550, 192040, 6490, 'NF', 'KYK5632', 'B', null, '029') -receber values(6577, '33336', '01503563', '2011/10/11', '2011/11/01', 154800, 160990, 6190, 'NF', 'KYV1613', 'B', null, '029') -receber values(6578, '33336', '01504630', '2011/10/13', '2011/11/03', 228900, 235770, 6870, 'NF', 'KYV1613', 'B', null, '029') -receber values(6579, '33336', '01506805', '2011/10/18', '2011/11/08', 754150, 757920, 3770, 'NF', 'KYV1613', 'T', null, '029') -receber values(11373, '33336', '01510543', '2011/10/25', '2011/11/15', 144960, 150030, 5070, 'NF', 'KYV1613', 'B', null, '029') -receber values(21967, '33336', '01518193', '2011/11/08', '2011/11/29', 399340, 399340, 0, 'NF', 'KYV1613', 'T', null, '029') -receber values(6580, '33337', '01500925', '2011/10/06', '2011/10/27', 1379020, 1379020, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6581, '33337', '01505570', '2011/10/14', '2011/11/04', 626400, 626400, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6582, '33337', '01506283', '2011/10/17', '2011/11/07', 1637400, 1653770, 16370, 'NF', 'KYK5632', 'T', null, '029') -receber values(6583, '33337', '01506284', '2011/10/17', '2011/11/06', 2291120, 2325490, 34370, 'NF', 'KYK5632', 'T', null, '029') -receber values(6584, '33337', '01506285', '2011/10/17', '2011/11/06', 606340, 615440, 9100, 'NF', 'KYK5632', 'T', null, '029') -receber values(11374, '33337', '01509890', '2011/10/24', '2011/11/14', 2897800, 2926780, 28980, 'NF', 'KYK5632', 'B', null, '029') -receber values(16327, '33337', '01513380', '2011/10/31', '2011/11/21', 2934200, 2948870, 14670, 'NF', 'KYK5632', 'B', null, '029') -receber values(21968, '33337', '01517453', '2011/11/07', '2011/11/28', 318000, 318000, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21969, '33337', '01517454', '2011/11/07', '2011/11/28', 1726300, 1726300, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(28159, '33337', '01524283', '2011/11/18', '2011/12/09', 1102800, 1102800, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1500, '33395', '01504703', '2011/10/13', '2011/10/23', 945710, 950440, 4730, 'NF', 'KYK5632', 'B', null, '029') -receber values(6585, '33395', '01505893', '2011/10/16', '2011/10/26', 248800, 248800, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(6586, '33395', '01508175', '2011/10/20', '2011/10/30', 486950, 496690, 9740, 'NF', 'KYK5632', 'B', null, '029') -receber values(6587, '33395', '01509266', '2011/10/23', '2011/11/02', 736800, 740480, 3680, 'NF', 'CXV9668', 'B', null, '029') -receber values(13737, '33395', '01511956', '2011/10/27', '2011/11/06', 520930, 520930, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(18903, '33395', '01515524', '2011/11/03', '2011/11/13', 322040, 326870, 4830, 'NF', 'KYK5632', 'B', null, '029') -receber values(21970, '33395', '01518258', '2011/11/08', '2011/11/18', 459500, 459500, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(21971, '33395', '01522001', '2011/11/15', '2011/11/25', 706210, 706210, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28160, '33395', '01523443', '2011/11/17', '2011/11/27', 477200, 477200, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(28161, '33395', '01524628', '2011/11/20', '2011/11/30', 1266200, 1266200, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(33207, '33395', '01527932', '2011/11/25', '2011/12/05', 488360, 488360, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(34030, '33395', '01528778', '2011/11/28', '2011/12/08', 182030, 182030, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(1501, '33494', '01503947', '2011/10/12', '2011/10/22', 248940, 251430, 2490, 'NF', 'KYK5632', 'B', null, '029') -receber values(16328, '33494', '01514923', '2011/11/02', '2011/11/12', 256770, 261910, 5140, 'NF', 'KYK5632', 'B', null, '029') -receber values(28162, '33494', '01522686', '2011/11/16', '2011/11/26', 223150, 223150, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(33208, '33494', '01528261', '2011/11/27', '2011/12/07', 117800, 117800, 0, 'NF', 'KYK5632', 'T', null, '029') -receber values(6588, '34072', '01497148', '2011/09/29', '2011/10/27', 2213520, 2213520, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6589, '34072', '01500947', '2011/10/06', '2011/11/03', 334300, 334300, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(6590, '34072', '01504767', '2011/10/13', '2011/11/10', 1023430, 1023430, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(13738, '34072', '01512046', '2011/10/27', '2011/11/24', 120400, 120400, 0, 'NF', 'LBT3689', 'B', null, '029') -receber values(21972, '34072', '01515576', '2011/11/03', '2011/12/01', 1171970, 1171970, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(32174, '34072', '01527205', '2011/11/24', '2011/12/22', 547400, 547400, 0, 'NF', 'LBT3689', 'T', null, '029') -receber values(6591, '34302', '01505636', '2011/10/14', '2011/10/24', 486320, 486320, 0, 'NF', 'CXV9668', 'B', null, '029') -receber values(18904, '34302', '01515548', '2011/11/03', '2011/11/13', 806050, 818140, 12090, 'NF', 'CXV9668', 'B', null, '029') -receber values(6592, '34361', '01505426', '2011/10/14', '2011/10/24', 247260, 247260, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6593, '34361', '01506095', '2011/10/17', '2011/10/27', 248190, 248190, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6594, '34361', '01506741', '2011/10/18', '2011/10/28', 202450, 202450, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(6595, '34361', '01509218', '2011/10/23', '2011/11/02', 407640, 409680, 2040, 'NF', 'LQO1343', 'B', null, '029') -receber values(12856, '34361', '01511211', '2011/10/26', '2011/11/05', 323330, 323330, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(16329, '34361', '01513403', '2011/10/31', '2011/11/10', 798580, 798580, 0, 'NF', 'LQO1343', 'B', null, '029') -receber values(34031, '34361', '01528632', '2011/11/28', '2011/12/08', 104400, 104400, 0, 'NF', 'LQO1343', 'T', null, '029') -receber values(6596, '34686', '01504712', '2011/10/13', '2011/10/27', 373640, 381110, 7470, 'NF', 'KYK5632', 'B', null, '029') -receber values(6597, '34686', '01505571', '2011/10/14', '2011/10/28', 292490, 296880, 4390, 'NF', 'KYK5632', 'B', null, '029') -receber values(6598, '34686', '01508921', '2011/10/21', '2011/11/04', 507640, 507640, 0, 'NF', 'KYK5632', 'B', null, '029') -receber values(16330, '34686', '01513375', '2011/10/31', '2011/11/14', 2397610, 2493510, 95900, 'NF', 'KYK5632', 'T', null, '029') -receber values(13739, '35005', '01511980', '2011/10/27', '2011/11/03', 72760, 74940, 2180, 'NF', 'KMM5715', 'T', null, '029') -receber values(28163, '35027', '01524633', '2011/11/20', '2011/11/30', 213200, 213200, 0, 'NF', 'KYK5632', 'B', null, '029') \ No newline at end of file diff --git a/LitebaseSDK/src/native/Constants.h b/LitebaseSDK/src/native/Constants.h deleted file mode 100644 index 7227872eab..0000000000 --- a/LitebaseSDK/src/native/Constants.h +++ /dev/null @@ -1,521 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// Constants.h,v 1.1.2.32 2010-08-25 21:34:46 juliana Exp - -/** - * Defines all constants used by Litebase. - */ - -#ifndef LITEBASE_CONSTANTS_H -#define LITEBASE_CONSTANTS_H - -// Table files. -#define DB_EXT ".db" // Database files. -#define DBO_EXT ".dbo" // Database object files. -#define IDK_EXT ".idk" // Index b-tree files. - -// juliana@noidr_1: removed .idr files from all indices and changed its format. - -// Constants used in date format. -#define DATE_MDY 1 // rnovais@567_2: month day year. -#define DATE_DMY 2 // rnovais@567_2: day month year. -#define DATE_YMD 3 // rnovais@567_2: year month day. - -// juliana@114_9: the absence of primary key can't be zero because the rowid may be a primary key or it can have an index. -#define NO_PRIMARY_KEY -1 // nowosad@200 - Indicates if table does not have primary key. -#define NO_COLUMN_NAMES 0 // nowosad@200 - Indicates if table has column names info. - -// These numbers must be synchronized with ResultSetMetaData types. -#define UNDEFINED_TYPE -1 // Undefined type. -#define CHARS_TYPE 0 // CHARS type. -#define SHORT_TYPE 1 // SHORT type. -#define INT_TYPE 2 // INT type. -#define LONG_TYPE 3 // LONG type. -#define FLOAT_TYPE 4 // FLOAT type. -#define DOUBLE_TYPE 5 // DOUBLE type. -#define CHARS_NOCASE_TYPE 6 // CHARS type that has case insensitive comparison. -#define BOOLEAN_TYPE 7 // BOOLEAN type. Used for expressions. -#define DATE_TYPE 8 // rnovais@567_2: int -#define DATETIME_TYPE 9 // rnovais@567_2: stored as two int -#define BLOB_TYPE 10 // BLOB type. - -// Constants for tables and indices. -#define DEFAULT_ROW_INC 10 // The default record increment when growing the table file. -#define CACHE_SIZE 20 // The index cache size. -#define RECGROWSIZE 64 // The record increment for indices. -#define SECTOR_SIZE 512 // The record size used to calculate the number of keys per b-tree node. -#define MAX_IDX 65534 // The maximum number of nodes of an index. // juliana@noidr_2 -#define DBNAME_SIZE 41 // Space for the name of the table plus the identification of the index, if needed. -#define COMP_IDX_PK_SIZE 64 // The space for composed indices in the header of .db. -#define DEFAULT_HEADER 512 // The default header size. -#define VERSION_TABLE 203 // The current table format version. // juliana@230_12 - -// Aggregate Functions supported -#define FUNCTION_AGG_NONE -1 // No function. -#define FUNCTION_AGG_COUNT 0 // COUNT() -#define FUNCTION_AGG_MAX 1 // MAX() -#define FUNCTION_AGG_MIN 2 // MIN() -#define FUNCTION_AGG_AVG 3 // AVG() -#define FUNCTION_AGG_SUM 4 // SUM() - -// rnovais@568_10: supported DtaType Functions -#define FUNCTION_DT_NONE -1 // No function. -#define FUNCTION_DT_YEAR 0 // YEAR() -#define FUNCTION_DT_MONTH 1 // MONTH() -#define FUNCTION_DT_DAY 2 // DAY() -#define FUNCTION_DT_HOUR 3 // HOUR() -#define FUNCTION_DT_MINUTE 4 // MINUTE() -#define FUNCTION_DT_SECOND 5 // SECOND() -#define FUNCTION_DT_MILLIS 6 // MILLIS() -#define FUNCTION_DT_ABS 7 // rnovais@570_1: ABS() -#define FUNCTION_DT_UPPER 8 // rnovais@570_1: UPPER() -#define FUNCTION_DT_LOWER 9 // rnovais@570_1: LOWER() - -// where clause expression tree possible types. -#define WC_TYPE_AND_DIFF_RS 0 // It is an AND of expressions. -#define WC_TYPE_OR_DIFF_RS 1 // It is an OR of expressions. - -// juliana@250_8: now the maximum number of columns, fields, tables, etc is 254 instead of 128 except on palm. -// guich@561_1: maximums for the parsing process. -#define MAXIMUMS 254 // guich@561_1: maximums for the parsing process. - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -#ifdef ANDROID -#define MAX_OPEN_FILES 512 -#elif defined darwin -#define MAX_OPEN_FILES 128 -#elif defined POSIX -#define MAX_OPEN_FILES 1024 -#endif - -// Available operand types. -#define OP_NONE 0 // No operator. -#define OP_BOOLEAN_AND 1 // Boolean operator AND. -#define OP_BOOLEAN_OR 2 // Boolean operator OR. -#define OP_BOOLEAN_NOT 3 // Boolean operator NOT. -#define OP_REL_EQUAL 4 // Relational operator =. -#define OP_REL_DIFF 5 // Relational operator != or <>. -#define OP_REL_GREATER 6 // Relational operator >. -#define OP_REL_LESS 7 // Relational operator <. -#define OP_REL_GREATER_EQUAL 8 // Relational operator >=. -#define OP_REL_LESS_EQUAL 9 // Relational operator <=. -#define OP_STRING_LITERAL 10 // A string literal. -#define OP_NUMERIC_LITERAL 11 // A numerical literal. -#define OP_IDENTIFIER 12 // An identifier. -#define OP_PAT_MATCH_LIKE 13 // LIKE operator. -#define OP_PAT_MATCH_NOT_LIKE 14 // NOT LIKE operator. -#define OP_PAT_IS 15 // rnovais@200_1: IS operator. -#define OP_PAT_IS_NOT 16 // rnovais@200_1: IS NOT operator. -#define OP_PAT_NULL 17 // rnovais@200_1: NULL operand. - -/// Possible commands -#define CMD_CREATE_TABLE 1 // CREATE TABLE ... -#define CMD_CREATE_INDEX 2 // CREATE INDEX ... -#define CMD_DROP_TABLE 3 // DROP TABLE ... -#define CMD_DROP_INDEX 4 // DROP INDEX ... -#define CMD_ALTER_DROP_PK 5 // ALTER TABLE ... DROP PRIMAY KEY -#define CMD_ALTER_ADD_PK 6 // ALTER TABLE ... ADD PRIMARY KEY (...) -#define CMD_ALTER_RENAME_TABLE 7 // ALTER TABLE ... RENAME TO ... -#define CMD_ALTER_RENAME_COLUMN 8 // ALTER TABLE ... RENAME ... TO ... -#define CMD_ALTER_ADD_COLUMN 9 // ALTER TABLE ADD... (new column definition) // juliana@add_1 -#define CMD_SELECT 10 // SELECT ... -#define CMD_INSERT 11 // INSERT INTO ... -#define CMD_UPDATE 12 // UPDATE ... -#define CMD_DELETE 13 // DELETE ... - -#define HCROWID 108705909 // hash code for the rowid. -#define LOGS_INT 1280264019 // LOGS_INT = Convert.chars2int("LOGS"); -#define CRC32_SIZE 256 // The crc table size. - -#define MAX_TABLE_NAME_LENGTH 23 // Maximum table file name. - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -// Constants for keys. -#define VALREC_SIZE 4 // The size of the record of a key: always an int. -#define NO_VALUE 0xFFFFFFFF // Represents a key that has no values attached to it. -#define LEAF 0xFFFF // A leaf node. - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -// Column attributes. -#define ATTR_COLUMN_HAS_INDEX 1 // This column has an index. -#define ATTR_COLUMN_HAS_DEFAULT 2 // This column has default value. -#define ATTR_COLUMN_IS_NOT_NULL 4 // This column can't be null. -#define ATTR_COLUMN_HAS_NO_INDEX -2 // ~ATTR_COLUMN_HAS_INDEX // juliana@227_6 -#define ATTR_DEFAULT_AUX_ROWID -1 // rnovais@570_61: Auxiliar default rowid. - -// Constants that indicates what should be saved when saving table meta data. -#define TSMD_ONLY_DELETEDROWSCOUNT 1 // Saves only the deleted rows count and header information. -#define TSMD_ONLY_AUXROWID 2 //rnovais@570_61: Also save the auxiliary row id. -#define TSMD_ONLY_PRIMARYKEYCOL 3 // Also save the primary key column information. -#define TSMD_ATLEAST_INDEXES 4 // Also save index information. -#define TSMD_EVERYTHING 5 // Save everything. - -// Constants used to differenciate the kinds of tokens. -// juliana@parser_1: improved Litebase parser. -#define IS_ALPHA 1 // Is an alphabetic character [a..z]|[A..Z]. -#define IS_DIGIT 2 // Is a digit [0..9]. -#define IS_SIGN 4 // Is a sign (+ / -). -#define IS_END_NUM 8 // Is a numer type identifier (e|E|d|D|l|L). -#define IS_RELATIONAL 16 // Is a relational operator: '>', '<', and '!'. -#define IS_PUNCT 32 // Is a punctution symbol: ':', ',', '?', and '='. -#define IS_OPERATOR 64 // Is a operator symbol: '*', '(', ')'. -#define IS_ALPHA_DIGIT 3 // (IS_ALPHA|IS_DIGIT|'_'). -#define IS_START_DIGIT 6 // (IS_DIGIT|IS_SIGN). -#define PARSER_EOF -1 // End of file. -#define PARSER_ERROR -2 // Parser error. - -// Reserved words. -#define NUM_RESERVED 61 // Number of reserved words. -#define HT_ABS 96370 // ABS reserved word hash code. -#define HT_ADD 96417 // ADD reserved word hash code. -#define HT_ALTER 92913686 // ALTER reserved word hash code. -#define HT_AND 96727 // AND reserved word hash code. -#define HT_AS 3122 // AS reserved word hash code. -#define HT_ASC 96881 // ASC reserved word hash code. -#define HT_AVG 96978 // AVG reserved word hash code. -#define HT_BLOB 3026845 // BLOB reserved word hash code. -#define HT_BY 3159 // BY reserved word hash code. -#define HT_CHAR 3052374 // CHAR reserved word hash code. -#define HT_COUNT 94851343 // COUNT reserved word hash code. -#define HT_CREATE -1352294148 // CREATE reserved word hash code. -#define HT_DATE 3076014 // DATE reserved word hash code. -#define HT_DATETIME 1793702779 // DATETIME reserved word hash code. -#define HT_DAY 99228 // DAY reserved word hash code. -#define HT_DEFAULT 1544803905 // DEFAULT reserved word hash code. -#define HT_DELETE -1335458389 // DELETE reserved word hash code. -#define HT_DESC 3079825 // DESC reserved word hash code. -#define HT_DISTINCT 288698108 // DISTINCT reserved word hash code. -#define HT_DOUBLE -1325958191 // DOUBLE reserved word hash code. -#define HT_DROP 3092207 // DROP reserved word hash code. -#define HT_FLOAT 97526364 // FLOAT reserved word hash code. -#define HT_FROM 3151786 // FROM reserved word hash code. -#define HT_GROUP 98629247 // GROUP reserved word hash code. -#define HT_HAVING -1224334299 // HAVING reserved word hash code. -#define HT_HOUR 3208676 // HOUR reserved word hash code. -#define HT_INDEX 100346066 // INDEX reserved word hash code. -#define HT_INSERT -1183792455 // INSERT reserved word hash code. -#define HT_INT 104431 // INT reserved word hash code. -#define HT_INTO 3237472 // INTO reserved word hash code. -#define HT_IS 3370 // IS reserved word hash code. -#define HT_KEY 106079 // KEY reserved word hash code. -#define HT_LIKE 3321751 // LIKE reserved word hash code. -#define HT_LONG 3327612 // LONG reserved word hash code. -#define HT_LOWER 103164673 // LOWER reserved word hash code. -#define HT_MAX 107876 // MAX reserved word hash code. -#define HT_MILLIS -1074095546 // MILLIS reserved word hash code. -#define HT_MIN 108114 // MIN reserved word hash code. -#define HT_MINUTE -1074026988 // MINUTE reserved word hash code. -#define HT_MONTH 104080000 // MONTH reserved word hash code. -#define HT_NOCASE -1040203663 // NOCASE reserved word hash code. -#define HT_NOT 109267 // NOT reserved word hash code. -#define HT_NULL 3392903 // NULL reserved word hash code. -#define HT_ON 3551 // ON reserved word hash code. -#define HT_OR 3555 // OR reserved word hash code. -#define HT_ORDER 106006350 // ORDER reserved word hash code. -#define HT_PRIMARY -314765822 // PRIMARY reserved word hash code. -#define HT_RENAME -934594754 // RENAME reserved word hash code. -#define HT_SECOND -906279820 // SECOND reserved word hash code. -#define HT_SELECT -906021636 // SELECT reserved word hash code. -#define HT_SET 113762 // SET reserved word hash code. -#define HT_SHORT 109413500 // SHORT reserved word hash code. -#define HT_SUM 114251 // SUM reserved word hash code. -#define HT_TABLE 110115790 // TABLE reserved word hash code. -#define HT_TO 3707 // TO reserved word hash code. -#define HT_UPDATE -838846263 // UPDATE reserved word hash code. -#define HT_UPPER 111499426 // UPPER reserved word hash code. -#define HT_VALUES -823812830 // VALUES reserved word hash code. -#define HT_VARCHAR 236613373 // VARCHAR reserved word hash code. -#define HT_WHERE 113097959 // WHERE reserved word hash code. -#define HT_YEAR 3704893 // YEAR reserved word hash code. - -#define MAX_RESERVED_SIZE 9 // The maximum size of a reserved word. - -// Parser tokens. -// juliana@parser_1: improved Litebase parser. -#define TK_CHAR 0 // CHAR reserved word token. -#define TK_SHORT 1 // SHORT reserved word token. -#define TK_INT 2 // INT reserved word token. -#define TK_LONG 3 // LONG reserved word token. -#define TK_FLOAT 4 // FLOAT reserved word token. -#define TK_DOUBLE 5 // DOUBLE reserved word token. -#define TK_VARCHAR 6 // VARCHAR reserved word token. -#define TK_NOCASE 7 // NOCASE reserved word token. -#define TK_DATE 8 // DATE reserved word token. -#define TK_DATETIME 9 // DATETIME reserved word token. -#define TK_BLOB 10 // BLOB reserved word token. -#define TK_ABS 11 // ABS reserved word token. -#define TK_ADD 12 // ADD reserved word token. -#define TK_ALTER 13 // ALTER reserved word token. -#define TK_AND 14 // AND reserved word token. -#define TK_AS 15 // AS reserved word token. -#define TK_ASC 16 // ASC reserved word token. -#define TK_AVG 17 // AVG reserved word token. -#define TK_BY 18 // BY reserved word token. -#define TK_COUNT 19 // COUNT reserved word token. -#define TK_CREATE 20 // CREATE reserved word token. -#define TK_DAY 21 // DAY reserved word token. -#define TK_DEFAULT 22 // DEFAULT reserved word token. -#define TK_DELETE 23 // DELETE reserved word token. -#define TK_DESC 24 // DESC reserved word token. -#define TK_DISTINCT 25 // DISTINCT reserved word token. -#define TK_DROP 26 // DROP reserved word token. -#define TK_FROM 27 // FROM reserved word token. -#define TK_GROUP 28 // GROUP reserved word token. -#define TK_HAVING 29 // HAVING reserved word token. -#define TK_HOUR 30 // HOUR reserved word token. -#define TK_INDEX 31 // INDEX reserved word token. -#define TK_INSERT 32 // INSERT reserved word token. -#define TK_INTO 33 // INTO reserved word token. -#define TK_IS 34 // IS reserved word token. -#define TK_KEY 35 // KEY reserved word token. -#define TK_LIKE 36 // LIKE reserved word token. -#define TK_LOWER 37 // LOWER reserved word token. -#define TK_MAX 38 // MAX reserved word token. -#define TK_MILLIS 39 // MILLIS reserved word token. -#define TK_OPEN 40 // '(' token. -#define TK_CLOSE 41 // ')' token. -#define TK_ASTERISK 42 // '*' token. -#define TK_MIN 43 // MIN reserved word token. -#define TK_COMMA 44 // ',' token. -#define TK_MINUTE 45 // MINUTE reserved word token. -#define TK_DOT 46 // '.' token. -#define TK_MONTH 47 // MONTH reserved word token. -#define TK_NOT 48 // NOT reserved word token. -#define TK_NULL 49 // NULL reserved word token. -#define TK_ON 50 // ON reserved word token. -#define TK_OR 51 // OR reserved word token. -#define TK_ORDER 52 // ORDER reserved word token. -#define TK_PRIMARY 53 // PRIMARY reserved word token. -#define TK_RENAME 54 // RENAME reserved word token. -#define TK_SECOND 55 // SECOND reserved word token. -#define TK_SELECT 56 // SELECT reserved word token. -#define TK_SET 57 // SET reserved word token. -#define TK_SUM 58 // SUM reserved word token. -#define TK_TABLE 59 // TABLE reserved word token. -#define TK_LESS 60 // '<' token. -#define TK_EQUAL 61 // '=' token. -#define TK_GREATER 62 // '>' token. -#define TK_INTERROGATION 63 // '?' token. -#define TK_TO 64 // TO reserved word token. -#define TK_UPDATE 65 // UPDATE reserved word token. -#define TK_UPPER 66 // UPPER reserved word token. -#define TK_VALUES 67 // VALUES reserved word token. -#define TK_WHERE 68 // WHERE reserved word token. -#define TK_YEAR 69 // YEAR reserved word token. -#define TK_IDENT 70 // Identifier token. -#define TK_STR 71 // String token. -#define TK_NUMBER 72 // Number token. -#define TK_GREATER_EQUAL 73 // '>=' token. -#define TK_LESS_EQUAL 74 // '<=' token. -#define TK_DIFF 75 // '<>' or '!=' token. - -// Litebase languages. -#define LANGUAGE_EN 1 // English language. -#define LANGUAGE_PT 2 // Portuguese language. - -// Litebase error messages. -// General errros. -#define ERR_MESSAGE_START 0 // "Error: " -#define ERR_MESSAGE_POSITION 1 // " Near position " -#define ERR_SYNTAX_ERROR 2 // "Syntax error." - -// Limit errors. -#define ERR_MAX_NUM_FIELDS_REACHED 3 // "Maximum number of different fields was reached." -#define ERR_MAX_NUM_PARAMS_REACHED 4 // "Maximum number of paramList in the 'WHERE/HAVING' clause was reached." -#define ERR_MAX_COMP_INDICES 5 // "Maximum number of composed indices 32 was reached." -#define ERR_MAX_TABLE_NAME_LENGTH 6 // "Table name too big: must be <= 23." -#define ERR_FIELDS_OVERFLOW 7 // "The maximum number of fields in a SELECT clause was exceeded." -#define ERR_FIELD_OVERFLOW_GROUPBY_ORDERBY 8 // "Maximum number of columns exceeded in the 'ORDER BY/GROUP BY' clause." - -// Column errors. -#define ERR_UNKNOWN_COLUMN 9 // "Unknown column " -#define ERR_INVALID_COLUMN_NAME 10 // "Invalid column name: " -#define ERR_INVALID_COLUMN_NUMBER 11 // "Invalid column number: " -#define ERR_COLUMN_DOESNOT_HAVE_AN_INDEX 12 // "The following column(s) does (do) not have an associated index " -#define ERR_AMBIGUOUS_COLUMN_NAME 13 // "Column name in field list is ambiguous: " -#define ERR_COLUMN_NOT_FOUND 14 // "Column not found: " -#define ERR_DUPLICATED_COLUMN_NAME 15 // "Duplicated column name: " - -// Primary key errors. -#define ERR_PRIMARY_KEY_ALREADY_DEFINED 16 // "A primary key was already defined for this table." -#define ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY 17 // "Table does not have a primary key." -#define ERR_STATEMENT_CREATE_DUPLICATED_PK 18 // "Statement creates a duplicated primary key in " - -// Type errors. -#define ERR_INCOMPATIBLE_TYPES 19 // "Incompatible types." -#define ERR_FIELD_SIZE_IS_NOT_INT 20 // "Field size must be a positive interger value." -#define ERR_INVALID_NUMBER 21 // "Value is not a valid number for the desired type: " -#define ERR_DATA_TYPE_FUNCTION 22 // "Incompatible data type for the function call: " - -// Number of fields errors. -#define ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH 23 // "The number of fields does not match the number of values " -#define ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION 24 // "The given number of values does not match the table definition." - -// Default value errors. -#define ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER 25 // "Length of default value is bigger than column size." -#define ERR_NOT_NULL_DEFAULT 26 // "An added column declared as NOT NULL must have a not null default value." - -// Driver errors. -#define ERR_DRIVER_CLOSED 27 // "This driver instance was closed and can't be used anymore. Please get a new instance of it." -#define ERR_RESULTSET_CLOSED 28 // "ResultSet already closed!" -#define ERR_RESULTSETMETADATA_CLOSED 29 // "ResultSetMetaData can't be used after the ResultSet is closed. " -#define ERR_INVALID_CRID 30 // "The application id must be four characters long." -#define ERR_INVALID_INC 31 // "The increment must be greater than 0 or -1." -#define ERR_ROWITERATOR_CLOSED 32 // "Iterator already closed." -#define ERR_PREPARED_STMT_CLOSED 33 // "Prepared statement closed. Please prepare it again." -#define ERR_INVALID_PARAMETER 34 // "Invalid connection parameter: %s". // juliana@253_11 - -// Table errors. -#define ERR_TABLE_NAME_NOT_FOUND 35 // "Table name not found: " -#define ERR_TABLE_ALREADY_CREATED 36 // "Table already created: " -#define ERR_WRONG_STRING_FORMAT 37 // "It is not possible to open a table within a connection with a different string format." -#define ERR_WRONG_CRYPTO_FORMAT 38 // "It is not possible to open a table within a connection with a different cryptography format." - -// ROWID errors. -#define ERR_ROWID_CANNOT_BE_CHANGED 39 // "ROWID can't be changed by the user!" - -// Prepared Statement errors. -#define ERR_QUERY_DOESNOT_RETURN_RESULTSET 40 // "SQL statement does not return result set." -#define ERR_QUERY_DOESNOT_PERFORM_UPDATE 41 // "SQL statement does not perform updates in the database." -#define ERR_NOT_ALL_PARAMETERS_DEFINED 42 // "Not all parameters of the query had their values defined." -#define ERR_PARAMETER_NOT_DEFINED 43 // "A value was not defined for the parameter " -#define ERR_INVALID_PARAMETER_INDEX 44 // "Invalid parameter index." - -// Rename errors. -#define ERR_TABLE_ALREADY_EXIST 45 // "Can't rename table. This table already exists: " -#define ERR_COLUMN_ALREADY_EXIST 46 // "Column already exists: " - -// Alias errors. -#define ERR_NOT_UNIQUE_ALIAS_TABLE 47 // "Not unique table/alias: " -#define ERR_DUPLICATE_ALIAS 48 // "This alias is already being used in this expression: " -#define ERR_REQUIRED_ALIAS 49 // "An alias is required for the aggregate function column." - -// Litebase.execute() error. -#define ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED 50 // "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute()." - -// Order by and group by errors. -#define ERR_ORDER_GROUPBY_MUST_MATCH 51 // "ORDER BY and GROUP BY clauses must match." -#define ERR_VIRTUAL_COLUMN_ON_GROUPBY 52 // "No support for virtual columns in SQL queries with GROUP BY clause." - -// Function errors. -#define ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT 53 // "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause." - -// " is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation functions." -#define ERR_IS_NOT_AGGREG_FUNCTION 54 - -// "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause." -#define ERR_CANNOT_MIX_AGGREG_FUNCTION 55 - -#define ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY 56 // "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause." - -// " was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation funtions." -#define ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION 57 - -#define ERR_SUM_AVG_WITH_DATE_DATETIME 58 // "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields." - -// DATE and DATETIME errors. -#define ERR_VALUE_ISNOT_DATE 59 // "Value is not a DATE: " -#define ERR_VALUE_ISNOT_DATETIME 60 // "Value is not a DATETIME: " - -// Index errors. -#define ERR_INDEX_ALREADY_CREATED 61 // "Index already created for column " -#define ERR_DROP_PRIMARY_KEY 62 // "Can't drop a primary key index with drop index." -#define ERR_INDEX_LARGE 63 // "Index too large. It can't have more than 32767 nodes." - -// NOT NULL errors. -#define ERR_PK_CANT_BE_NULL 64 // "Primary key can't have null." -#define ERR_FIELD_CANT_BE_NULL 65 // "Field can't be null: " -#define ERR_PARAM_NULL 66 // "A parameter in a where clause can't be null." - -// Result set errors. -#define ERR_RS_INV_POS 67 // "ResultSet in invalid record position: ." -#define ERR_RS_DEC_PLACES_START 68 // "Invalid value for decimal places: . It must range from -1 to 40." - -// File errors. -#define ERR_CANT_READ 69 // "Can't read from table " -#define ERR_CANT_LOAD_NODE 70 // "Can't load leaf node!" -#define ERR_TABLE_CORRUPTED 71 // "Table is corrupted: " -#define ERR_TABLE_NOT_CLOSED 72 // "Table not closed properly: " // juliana@220_2 -#define ERR_TABLE_CLOSED 73 // "A properly closed table can't be used in recoverTable(): " // juliana@222_2 -#define ERR_IDX_RECORD_DEL 74 // "Can't find index record position on delete." -#define ERR_WRONG_VERSION 75 // "The table format is incompatible with Litebase version. Please update your tables." -#define ERR_WRONG_PREV_VERSION 76 // "The table format is not the previous one: " -#define ERR_INVALID_PATH 77 // "Invalid path: " // juliana@214_1 -#define ERR_INVALID_POS 78 // "Invalid pos: " -#define ERR_DB_NOT_FOUND 79 // "Database not found." // juliana@226_10 -#define ERR_TABLE_OPENED 80 // "An opened table can't be recovered or converted: " // juliana@230_12 - -// BLOB errors. -#define ERR_BLOB_TOO_BIG 81 // "The total size of a blob can't be greater then 10 Mb." -#define ERR_INVALID_MULTIPLIER 82 // "This is not a valid size multiplier." -#define ERR_BLOB_PRIMARY_KEY 83 // "A blob type can't be part of a primary key." -#define ERR_BLOB_INDEX 84 // "A BLOB column can't be indexed." -#define ERR_BLOB_WHERE 85 // "A BLOB can't be in the where clause." -#define ERR_BLOB_STRING 86 // "A BLOB can't be converted to a string." -#define ERR_BLOB_ORDER_GROUP 87 // "Blobs types can't be in ORDER BY or GROUP BY clauses. -#define ERR_COMP_BLOBS 88 // "It is not possible to compare BLOBs." -#define ERR_BLOBS_PREPARED 89 // "It is only possible to insert or update a BLOB through prepared statements." - -#define TOTAL_ERRORS 90 // Total Litebase possible errors. - -#define MAX_NUM_INDEXES_APPLIED 32 // The maximum number of indexes to be applied. - -// Pattern matching types. -#define PAT_MATCH_STARTS_WITH 1 // %... -#define PAT_MATCH_ENDS_WITH 2 // ...% -#define PAT_MATCH_CONTAINS 3 // %...% -#define PAT_MATCH_ANYTHING 4 // % -#define PAT_MATCH_MIDDLE 5 // rnovais@568_1: accept % in the middle: ...%... -#define PAT_MATCH_EQUAL 6 // rnovais@568_1: accept without %. - -#define PAT_MATCH_CHAR_ZERO_MORE '%' // Pattern matching characters - -// Possibles type for the select clause. -#define COUNT_WITH_WHERE 1 // select count(*) from table_name where ... - -#define CACHE_INITIAL_SIZE 2048 // Table files initial cache size. -#define INDEX_SORT_MAX_TIME 40 // The maximum time (in seconds) that will be taken to sort a table before creating the index. - -// Join operation constants. -#define NO_RECORD 0 // The end of the table. -#define VALIDATION_RECORD_OK 1 // The row can be used. -#define VALIDATION_RECORD_NOT_OK 2 // The row can't be used. -#define VALIDATION_RECORD_INCOMPLETE 3 // Must continue the validation. -#define VALIDATION_RECORD_INCOMPLETE_OK 4 // Used internally on booleanTreeEvaluateJoin(). The current branch was validated as true. - -// guich@_300: addes support for basic synchronization. -#define ROW_ATTR_SYNCED 0X00000000L // Indicates if the a row was synced. -#define ROW_ATTR_NEW 0X40000000L // Indicates if the row is new. -#define ROW_ATTR_UPDATED 0x80000000L // Indicates if the row was updated. -#define ROW_ATTR_DELETED 0XC0000000L // Indicates if the row was deleted. -#define ROW_ID_MASK 0x3FFFFFFFL // The rowid mask. -#define ROW_ATTR_MASK 0xC0000000L // The row attributes mask. -#define ROW_ATTR_SHIFT 30L // The shift for the row attributes. - -// juliana@230_16: solved a bug with row iterator. -#define ROW_ATTR_SYNCED_MASK 0 // Indicates if the a row was synced. -#define ROW_ATTR_DELETED_MASK 3 // Indicates if the row was deleted. - -// juliana@210_2: now Litebase supports tables with ascii strings. -// Flags for saving the table. -#define IS_SAVED_CORRECTLY 1 // Indicates that a table was saved correctly. -#define IS_ASCII 2 // Indicates that the table strings are to be saved in the ascii format. -#define USE_CRYPTO 3 // Indicates that the table uses weak cryptography. - -// Numerical limits. -#define MIN_SHORT_VALUE (int16)-32768 // The minimum short value: -32768. -#define MAX_SHORT_VALUE (int16)32767 // The maximum short value: 32767. - -#ifdef POSIX // juliana@226_18: minimum float and double values for POSIX are different from IEEE values. - #define MIN_FLOAT_VALUE 1.17549435e-38 - #define MIN_DOUBLE_VALUE 2.2250738585072014e-308 -#else - #define MIN_FLOAT_VALUE 1.4e-45 // The minimum float value: 1.4e-45f. - #define MIN_DOUBLE_VALUE 4.9E-324 // The minimum double value: 4.9E-324. -#endif -#define MAX_FLOAT_VALUE 3.4028235e+38 // The maximum float value: 3.4028235e+38f. - -#endif diff --git a/LitebaseSDK/src/native/Index.c b/LitebaseSDK/src/native/Index.c deleted file mode 100644 index 766ee99a2d..0000000000 --- a/LitebaseSDK/src/native/Index.c +++ /dev/null @@ -1,1309 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * Defines functions to deal a B-Tree header. This has the implementation of a B-Tree. It is used to store the table indexes. It has some - * improvements for memory usage, disk space, and speed, targetting the creation of indexes, where the table's record is far greater than the index - * record. - */ - -#include "Index.h" - -/** - * Creates a composed index. - * - * @param id The index id. - * @param columns The columns of this index. - * @param numberColumns The number of columns of this index. - * @param heap The heap to allocate the compoded index structure. - * @return The composed index. - */ -ComposedIndex* createComposedIndex(int32 id, uint8* columns, int32 numberColumns, Heap heap) -{ - TRACE("createComposedIndex") - ComposedIndex* compIndex = (ComposedIndex*)TC_heapAlloc(heap, sizeof(ComposedIndex)); - compIndex->indexId = id; - compIndex->columns = columns; - compIndex->numberColumns = numberColumns; - return compIndex; -} - -/** - * Constructs an index structure. - * - * @param context The thread context where the function is being executed. - * @param table The table of the index. - * @param keyTypes The types of the columns of the index. - * @param colSizes The column sizes. - * @param name The name of the index table. - * @param numberColumns The number of columns of the index. - * @param exist Indicates that the index files already exist. - * @param heap A heap to allocate the index structure. - * @return The index created or null if an error occurs. - * @throws DriverException If is not possible to create the index files. - */ -Index* createIndex(Context context, Table* table, int8* keyTypes, int32* colSizes, CharP name, int32 numberColumns, bool exist, Heap heap) -{ - TRACE("createIndex") - Index* index = (Index*)TC_heapAlloc(heap, sizeof(Index)); - int32 keyRecSize = VALREC_SIZE; - char buffer[DBNAME_SIZE]; - TCHARP sourcePath = table->sourcePath; - XFile* fnodes = &index->fnodes; - - index->numberColumns = numberColumns; - index->table = table; - index->types = keyTypes; - index->colSizes = colSizes; - xstrcpy(index->name, name); - - while (--numberColumns >= 0) // Gets the key sizes for each column of the index. - keyRecSize += typeSizes[keyTypes[numberColumns]]; - - index->btreeMaxNodes = (SECTOR_SIZE - 5) / (keyRecSize + 2); - - // int size + key[k] + (Node = int)[k+1] - index->nodeRecSize = 2 + index->btreeMaxNodes * (index->keyRecSize = keyRecSize) + ((index->btreeMaxNodes + 1) << 1); - - index->heap = heap; - -// juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - index->firstLevel = (Node**)TC_heapAlloc(heap, index->btreeMaxNodes * TSIZE); // Creates the first index level. - - // juliana@223_14: solved possible memory problems. - // Creates the root node. - index->root = createNode(index); - // juliana@230_32 - index->root->idx = 0; - - xstrcpy(buffer, name); - xstrcat(buffer, IDK_EXT); - - // juliana@253_8: now Litebase supports weak cryptography. - if (!nfCreateFile(context, buffer, !exist, table->db.db.useCrypto, sourcePath, fnodes, index->nodeRecSize << 1)) - return null; - - index->nodeCount = index->fnodes.size / index->nodeRecSize; - - if (index->fnodes.size) - if (!nodeLoad(context, index->root)) - { - nfClose(context, &index->fnodes); - return null; - } - return index; -} - -/** - * Creates an index. - * - * @param context The thread context where the function is being executed. - * @param table The table name whose index is to be created. - * @param columnHashes The hashes of the index columns. - * @param isPKCreation Indicates if the index to be created is the primary key. - * @param indexCount The column numbers of the index. - * @param composedPKCols The columns of the composed primary key. - * @return false if an error occured; true, otherwise. - * @throws DriverException If a column for the index does not exist or is of type blob. - * @throws SQLParseException If a column for the index is of type blob. - */ -bool driverCreateIndex(Context context, Table* table, int32* columnHashes, bool isPKCreation, int32 indexCount, uint8* composedPKCols) -{ - TRACE("driverCreateIndex") - int32 idx = -1, - i, - saveType, - newIndexNumber, - size = indexCount << 2; - bool ret = true; - Heap heap = heapCreate(); - PlainDB* plainDB = &table->db; - uint8* columns; - int32* columnSizes; - int8* columnTypes; - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - columns = (uint8*)TC_heapAlloc(heap, indexCount); - columnSizes = (int32*)TC_heapAlloc(heap, size); - columnTypes = (int8*)TC_heapAlloc(heap, indexCount); - - i = indexCount; - while (--i >= 0) - { - // juliana@222_3: Corrected a small issue that would make a DriverException not to be thrown when creating an index in a non-existing column on - // Windows 32, Windows CE, Palm OS, iPhone, and Android. - columns[i] = idx = (composedPKCols? composedPKCols[i] : TC_htGet32Inv(&table->htName2index, columnHashes[i])); - if (idx < 0) // Column not found. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), ""); - goto error; - } - - columnSizes[i] = table->columnSizes[idx]; - if ((columnTypes[i] = table->columnTypes[idx]) == BLOB_TYPE) // An index can't have a blob column. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_BLOB_INDEX)); - table->primaryKeyCol = NO_PRIMARY_KEY; - goto error; - } - } - - newIndexNumber = verifyIfIndexAlreadyExists(context, table, columns, indexCount); - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - if (!setModified(context, table)) - goto error; - - if (indexCount == 1) - { - if (newIndexNumber < 0 || !indexCreateIndex(context, table, table->name, columns[0], columnSizes, columnTypes, false, heap)) - goto error; - - saveType = TSMD_ATLEAST_INDEXES; - } - else - { - if (newIndexNumber < 0 - || !indexCreateComposedIndex(context, table, table->name, columns, columnSizes, columnTypes, indexCount, newIndexNumber, true, false, heap)) - goto error; - saveType = TSMD_EVERYTHING; - } - - // guich@555_4: changed from 1 to 0 because this is now a row count, not a record count. - if (plainDB->rowCount > 0) // The header may be created but the table may still be empty - { - // Catchs the PrimaryKeyViolation exception to drop the recreated index. - if (indexCount == 1) - { - if (!tableReIndex(context, table, idx, isPKCreation, null)) - { - driverDropIndex(context, table, idx); - if (table->primaryKeyCol == idx) // juliana@114_9 - table->primaryKeyCol = NO_PRIMARY_KEY; // no return: we must save the metadata - ret = false; - } - } - else - { - i = (newIndexNumber < 0)? -newIndexNumber : newIndexNumber; - if (isPKCreation) - table->composedPK = i - 1; - if (!tableReIndex(context, table, -1, isPKCreation, table->composedIndexes[i - 1])) - { - driverDropComposedIndex(context, table, table->composedPrimaryKeyCols, table->numberComposedPKCols, i - 1, true); - if (isPKCreation) - { - table->composedPK = NO_PRIMARY_KEY; - table->numberComposedPKCols = 0; - } - ret = false; - } - } - } - return tableSaveMetaData(context, table, saveType) && ret; // guich@560_24: saves table meta data. - -error: - heapDestroy(heap); - return false; -} - -/** - * Removes a value from the index. - * - * @param context The thread context where the function is being executed. - * @param key The key to be removed. - * @param record The record being removed. - * @return true If the value was removed; false otherwise. - * @throws DriverException If its not possible to find the key record to delete or the index is corrupted. - */ -bool indexRemoveValue(Context context, Key* key, int32 record) -{ - TRACE("indexRemoveValue") - Index* index = key->index; - - if (index->fnodes.size) - { - Node* curr = index->root; // 0 is always the root. - int32 nodeCounter = index->nodeCount, - pos, - numberColumns = index->numberColumns, - size, - count = 0; - Key* keyFound; - Key* currKeys; - uint16* children; - PlainDB* plainDB = &index->table->db; - int32* vector = index->table->nodes; - - while (true) - { - keyFound = &(currKeys = curr->keys)[pos = nodeFindIn(context, curr, key, false)]; // juliana@201_3 // Finds the key position. - children = curr->children; - - if (pos < (size = curr->size) && keyEquals(context, key, keyFound, numberColumns, plainDB)) - { - while (pos >= 0 && keyEquals(context, key, (keyFound = &currKeys[pos]), numberColumns, plainDB) - && (keyFound->record >= record || keyFound->record == NO_VALUE)) - pos--; - while (++pos < size && keyEquals(context, key, (keyFound = &currKeys[pos]), numberColumns, plainDB) - && (keyFound->record <= record || keyFound->record == NO_VALUE)) - { - if (keyFound->record == record) - { - keyFound->record = NO_VALUE; - if (!nodeSaveDirtyKey(context, curr, pos)) // no break! - return false; - return true; - } - - if (keyFound->record == NO_VALUE && !nodeIsLeaf(curr)) - vector[count++] = children[pos]; - } - } - - if (!nodeIsLeaf(curr)) // If there are children, load them if the key was not found yet. - vector[count++] = children[pos]; - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (count) - { - if (!(curr = indexLoadNode(context, index, vector[--count]))) - return false; - } - else - break; - } - } - - // Could not find the key record to be deleted. - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_IDX_RECORD_DEL)); - return false; -} - -/** - * Loads a node. - * - * @param context The thread context where the function is being executed. - * @param index The index. - * @param idx The index of the value to be loaded. - * @return The node or null in case of an error. - * @throws DriverException If the index is corrupted. - */ -Node* indexLoadNode(Context context, Index* index, int32 idx) -{ - TRACE("indexLoadNode") - int32 i = CACHE_SIZE; - Node* cand; - Node** nodes; - - if (!idx) // If the index is 0, return the root. - return index->root; - if (idx == LEAF) // If the node is a leaf, the index is corrupted. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return null; - } - - // Tries to find the node in the nodes of the first level. - if (idx <= index->btreeMaxNodes) - { - if (!(cand = (nodes = index->firstLevel)[idx - 1])) - { - (cand = nodes[idx - 1] = createNode(index))->idx = idx; - nodeLoad(context, cand); - } - else if (cand->idx == (uint16)-1) - { - cand->idx = idx; - nodeLoad(context, cand); - } - return cand; - } - - // Loads the cache if the node is in a deeper level. - nodes = index->cache; - while (--i >= 0) // Loads the cache. - if (nodes[i] && nodes[i]->idx == idx) - return nodes[index->cacheI = i]; - - if (++index->cacheI >= CACHE_SIZE) - index->cacheI = 0; - - IF_HEAP_ERROR(index->heap) // juliana@223_14: solved possible memory problems. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - - // juliana@230_25: solved a bug with index with repeated keys which could not be built correctly. - if (!(cand = nodes[index->cacheI])) - cand = nodes[index->cacheI] = createNode(index); - - if (index->isWriteDelayed && cand->isDirty && nodeSave(context, cand, false, 0, cand->size) < 0) // Saves this one if it is dirty. - return null; - cand->idx = idx; - - // Loads the node. - if (!nodeLoad(context, cand)) - return null; - return cand; -} - -/** - * Finds the given key and make the monkey climb on the values. - * - * @param context The thread context where the function is being executed. - * @param key The key to be found. - * @param markBits The rows which will be returned to the result set. - * @return false if an error occured; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool indexGetValue(Context context, Key* key, MarkBits* markBits) -{ - TRACE("indexGetValue") - Index* index = key->index; - - if (index->fnodes.size) - { - Node* curr = index->root; // 0 is always the root. - int32 nodeCounter = index->nodeCount, - numberColumns = index->numberColumns, - pos, - size, - count = 0; - Key* keyFound; - Key* currKeys; - uint16* children; - PlainDB* plainDB = &index->table->db; - int32* vector = index->table->nodes; - - while (true) - { - keyFound = &(currKeys = curr->keys)[pos = nodeFindIn(context, curr, key, false)]; // juliana@201_3 // Finds the key position. - children = curr->children; - - // juliana@284_2: solved a possible insertion of a duplicate value in a PK. - if (pos < (size = curr->size) && keyEquals(context, key, keyFound, numberColumns, plainDB)) - { - if (!markBits) - if (keyFound->record != NO_VALUE) - { - TC_throwExceptionNamed(context, "litebase.PrimaryKeyViolationException", getMessage(ERR_STATEMENT_CREATE_DUPLICATED_PK), - index->table->name); - return false; - } - do - pos--; - while (pos >= 0 && keyEquals(context, key, &currKeys[pos], numberColumns, plainDB)); - while (++pos < size && keyEquals(context, key, &currKeys[pos], numberColumns, plainDB)) - { - if (markBits) - if (onKey(context, &currKeys[pos], markBits) == -1) - return false; - if (!nodeIsLeaf(curr)) - vector[count++] = children[pos]; - } - } - if (!nodeIsLeaf(curr)) // If there are children, load them if the key was not found yet. - vector[count++] = children[pos]; - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (count) - { - if (!(curr = indexLoadNode(context, index, vector[--count]))) - return false; - } - else - break; - } - } - return true; -} - -/** - * Climbs on the nodes that are greater or equal than the current one. - * - * @param context The thread context where the function is being executed. - * @param node The node to be compared with. - * @param start The first key of the node to be searched. - * @param markBits The rows which will be returned to the result set. - * @param stop Indicates when the climb process can be finished. - * @return If it has to stop the climbing process or not, or false if an error occured. - */ -bool indexClimbGreaterOrEqual(Context context, Node* node, int32 start, MarkBits* markBits, bool* stop) -{ - TRACE("indexClimbGreaterOrEqual") - int32 ret, - size = node->size; - uint16* children = node->children; - Key* keys = node->keys; - Index* index = node->index; - - if (start >= 0) - { - *stop = !(ret = onKey(context, &keys[start], markBits)); - if (ret == -1) - return false; - } - if (nodeIsLeaf(node)) - while (!(*stop) && ++start < size) - { - *stop = !(ret = onKey(context, &keys[start], markBits)); - if (ret == -1) - return false; - } - else - { - Node* curr; - Node* loaded; - - if (index->nodesArrayCount > 0) - curr = (Node*)index->nodes[--index->nodesArrayCount]; - else - curr = createNode(index); // juliana@230_32: corrected a bug of inequality searches in big indices not returning all the results. - - while (!(*stop) && ++start <= size) - { - if (!(loaded = getLoadedNode(context, index, children[start]))) - { - (loaded = curr)->idx = children[start]; - if (!nodeLoad(context, curr)) - return false; - } - if (!indexClimbGreaterOrEqual(context, loaded, -1, markBits, stop)) - return false; - if (start < size && !(*stop)) - { - *stop = !(ret = onKey(context, &node->keys[start], markBits)); - if (ret == -1) - return false; - } - } - index->nodes[index->nodesArrayCount++] = (size_t)curr; - } - return true; -} - -/** - * Starts from the root to find the left key, then climbs from it until the end. - * - * @param context The thread context where the function is being executed. - * @param left The left key. - * @param markBits The rows which will be returned to the result set. - * @return false if an error occured; true, otherwise. - * @throws DriverException If the index is corrupted. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - */ -bool indexGetGreaterOrEqual(Context context, Key* left, MarkBits* markBits) -{ - TRACE("indexGetGreaterOrEqual") - Index* index = left->index; - - if (index->fnodes.size) - { - int32 pos, - comp, - nodeCounter = index->nodeCount, - numberColumns = index->numberColumns; - uint32 size = 0; - int32* intVector1 = index->table->nodes; - Node* curr = index->root; // Starts from the root. - PlainDB* plainDB = &index->table->db; - Key* currKeys; - - while (true) - { - currKeys = curr->keys; - - if ((pos = nodeFindIn(context, curr, left, false)) < curr->size) // juliana@201_3 - { - while (--pos >= 0 && keyEquals(context, left, &currKeys[pos], numberColumns, plainDB)); - - // Compares left keys with curr keys. If this value is above or equal to the one being looked for, stores it. - if ((comp = keyCompareTo(context, left, &currKeys[++pos], numberColumns, plainDB)) <= 0) - { - intVector1[size++] = pos; - intVector1[size++] = curr->idx; - } - else if (comp >= 0) // left >= curr.keys[pos] ? - break; - } - if (nodeIsLeaf(curr)) // If there are children, load them if the key was not found yet. - break; - - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (!(curr = indexLoadNode(context, index, curr->children[pos]))) - return false; - } - if (size) - { - bool stop; - - // juliana@230_32: corrected a bug of inequality searches in big indices not returning all the results. - while (size) - { - stop = false; - if (!(curr = indexLoadNode(context, index, intVector1[--size])) - || !indexClimbGreaterOrEqual(context, curr, intVector1[--size], markBits, &stop)) - return false; - if (stop) - break; - } - } - } - return true; -} - -/** - * Splits the overflown node of this B-Tree. The stack ancestors contains all ancestors of the node, together with the known insertion position in - * each of these ancestors. - * - * @param context The thread context where the function is being executed. - * @param curr The current node. - * @param count The number of elements in the ancestors array. - * @return false if an error occured; true, otherwise. - */ -bool indexSplitNode(Context context, Node* curr, int32 count) -{ - TRACE("indexSplitNode") - Key* med; - Index* index = curr->index; - Node* root = index->root; - int32* ancestors = index->table->nodes; - - // guich@110_3: curr.size * 3/4 - note that medPos never changes, because the node is always split when the same size is reached. - // juliana@283_1: solved a bug which would buid corrupted indices when creating or recreating them. - int32 medPos = index->isOrdered? (curr->size - 2) : (curr->size / 2), - - btreeMaxNodes = index->btreeMaxNodes, - left, - right; - - while (curr) - { - med = &curr->keys[medPos]; - if ((right = nodeSave(context, curr, true, medPos + 1, curr->size)) < 0) // right sibling - must be the first one to save! - return false; - - if (curr->idx) // guich@110_4: not the root? reuses this node; cut it at medPos. - { - left = curr->idx; - curr->size = medPos; - if (nodeSave(context, curr, false, 0, curr->size) < 0 - || !(curr = indexLoadNode(context, index, ancestors[--count])) - || !nodeInsert(context, curr, med, left, right, ancestors[--count])) // Loads the parent. - return false; - if (curr->size < btreeMaxNodes) // Parent has not overflown? - break; - } - else - { - if ((left = nodeSave(context, curr, true, 0, medPos)) < 0) // Left sibling. - return false; - nodeSet(root, med, left, right); // Replaces the root record. - if (nodeSave(context, root, false, 0, root->size) < 0) - return false; - break; - } - } - return true; -} - -/** - * Removes the index files. - * - * @param context The thread context where the function is being executed. - * @param index The index to be removed. - * @return false if an error occured; true, otherwise. - */ -bool indexRemove(Context context, Index* index) -{ - TRACE("indexRemove") - Table* table = index->table; - - if (index->heap && !nfRemove(context, &index->fnodes, table->sourcePath)) - return false; - - heapDestroy(index->heap); - return true; -} - -/** - * Closes the index files. - * - * @param context The thread context where the function is being executed. - * @param index The index to be removed. - * @return false if an error occured; true, otherwise. - */ -bool indexClose(Context context, Index* index) -{ - TRACE("indexClose") - int32 ret; - - index->fnodes.finalPos = index->nodeCount * index->nodeRecSize; // Calculated the used space; the file will have no zeros at the end. - ret = nfClose(context, &index->fnodes); - heapDestroy(index->heap); - return ret; -} - -/** - * Empties the index files, since the rows were deleted. - * - * @param context The thread context where the function is being executed. - * @param index The index to be erased. - * @throws DriverException If it is not possible to truncate the index files. - * @return false if an error occured; true, otherwise. - */ -bool indexDeleteAllRows(Context context, Index* index) -{ - TRACE("indexDeleteAllRows") - int32 i; - Node** cache = index->cache; - Node** firstLevel = index->firstLevel; - XFile* fnodes = &index->fnodes; - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((i = reopenFileIfNeeded(context, fnodes))) - { - fileError(context, i, fnodes->name); - return false; - } -#endif - - // It is faster truncating a file than re-creating it again. - if ((i = lbfileSetSize(&fnodes->file, 0))) - { - fileError(context, i, fnodes->name); - return false; - } - - i = CACHE_SIZE; - while (--i >= 0) // Erases the cache. - if (cache[i]) - cache[i]->idx = -1; - - i = index->btreeMaxNodes; - while (--i >= 0) // Erases the first level nodes. - if (firstLevel[i]) - firstLevel[i]->idx = -1; - - // juliana@220_6: The node count should be reseted when recreating the indices. - index->cacheI = 0; - index->nodeCount = fnodes->size = fnodes->position = fnodes->finalPos = fnodes->cachePos = fnodes->cacheIsDirty = 0; - return true; -} - -/** - * Delays the write to disk, caching them at memory. - * - * @param context The thread context where the function is being executed. - * @param index The index. - * @param delayed Indicates if the writing process is to be done later or not. - * @return false if an error occured; true, otherwise. - */ -bool indexSetWriteDelayed(Context context, Index* index, bool delayed) -{ - TRACE(delayed ? "indexSetWriteDelayed on" : "indexSetWriteDelayed off") - int32 i; - bool ret = true; - Node** nodes; - - ret &= nodeSetWriteDelayed(context, index->root, delayed); // Commits the pending keys. - -// Commits the pending first level nodes. - i = index->btreeMaxNodes; - nodes = index->firstLevel; - while (--i >= 0) - ret &= nodeSetWriteDelayed(context, nodes[i], delayed); - - // Commits the pending cache nodes. - nodes = index->cache; - i = CACHE_SIZE; - while (--i >= 0) - ret &= nodeSetWriteDelayed(context, nodes[i], delayed); - - if (!delayed) // Shrinks the values. - ret &= nfGrowTo(context, &index->fnodes, index->nodeCount * index->nodeRecSize); - index->isWriteDelayed = delayed; - return ret; -} - -/** - * Adds a key to an index. - * - * @param context The thread context where the function is being executed. - * @param index The index where the key is going to be inserted. - * @param values The key to be inserted. - * @param record The record of the key in the table. - * @return false if an error occured; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool indexAddKey(Context context, Index* index, SQLValue** values, int32 record) -{ - TRACE("indexAddKey") - Key key; - SQLValue keys[MAXIMUMS + 1]; - Node* root = index->root; - bool splitting = false; - int32 numberColumns = index->numberColumns; - - key.keys = keys; - keySet(&key, values, index, numberColumns); // Sets the key. - - // Inserts the key. - if (!index->fnodes.size) - { - key.record = record; - nodeSet(root, &key, LEAF, LEAF); - if (nodeSave(context, root, true, 0, 1) < 0) - return false; - } - else - { - Node* curr = root; - Key* keyFound; - Key* currKeys; - PlainDB* plainDB = &index->table->db; - int32* ancestors = index->table->nodes; - int32 nodeCounter = index->nodeCount, - btreeMaxNodesLess1 = index->btreeMaxNodes - 1, - pos, - size, - count = 0; - - while (true) - { - keyFound = &(currKeys = curr->keys)[pos = nodeFindIn(context, curr, &key, true)]; // juliana@201_3 - if (pos < (size = curr->size) && keyEquals(context, &key, keyFound, numberColumns, plainDB)) - { - // juliana@281_1: corrected a possible index corruption. - while (pos >= 0 && keyEquals(context, &key, (keyFound = &currKeys[pos]), numberColumns, plainDB) - && (keyFound->record >= record || keyFound->record == NO_VALUE)) - pos--; - while (++pos < size && keyEquals(context, &key, (keyFound = &currKeys[pos]), numberColumns, plainDB) - && (keyFound->record < record || keyFound->record == NO_VALUE)); - } - - if (nodeIsLeaf(curr)) - { - // If the node will becomes full, the insert is done again, this time keeping track of the ancestors. Note: with k = 50 and 200000 - // values, there are about 1.1 million useless pushes without this redundant insert. - if (!splitting && curr->size == btreeMaxNodesLess1) - { - splitting = true; - curr = index->root; - count = 0; - nodeCounter = index->nodeCount; - } - else - { - key.record = record; - if (!nodeInsert(context, curr, &key, LEAF, LEAF, pos)) - return false; - if (splitting && !indexSplitNode(context, curr, count)) // Curr has overflown. - return false; - break; - } - } - else - { - if (splitting) - { - ancestors[count++] = pos; - ancestors[count++] = curr->idx; - } - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (!(curr = indexLoadNode(context, index, curr->children[pos]))) - return false; - } - } - } - return true; -} - -/** - * Renames the index files. - * - * @param context The thread context where the function is being executed. - * @param index The index which will be renamed. - * @param newName The new name for the index. - * @return false if an error occured; true, otherwise. - */ -bool indexRename(Context context, Index* index, CharP newName) -{ - TRACE("indexRename") - char buffer[DBNAME_SIZE]; - TCHARP sourcePath = index->table->sourcePath; - - // Renames the keys. - xstrcpy(index->name, newName); - xstrcpy(buffer, newName); - xstrcat(buffer, IDK_EXT); - if (!nfRename(context, &index->fnodes, buffer, sourcePath)) - return false; - - return true; -} - -/** - * Returns a node already loaded or loads it if there is empty space in the cache node to avoid loading already loaded nodes. - * - * @param context The thread context where the function is being executed. - * @param index The index where a node is going to be fetched. - * @return The loaded node, a new cache node with the requested node loaded, or null if it is not - * already loaded and its cache is full. - */ -Node* getLoadedNode(Context context, Index* index, int32 idx) -{ - TRACE("getLoadedNode") - Node* node; - Node** nodes; - int32 i = -1; - - // Tries to find the node in the nodes of the first level. - if (idx <= index->btreeMaxNodes) - { - if (!(node = (nodes = index->firstLevel)[idx - 1])) - { - (node = nodes[idx - 1] = createNode(index))->idx = idx; - nodeLoad(context, node); - } - else if (node->idx == (uint16)-1) - { - node->idx = idx; - nodeLoad(context, node); - } - return node; - } - - // Tries to get an already loaded node if it is a node from a deeper level. - nodes = index->cache; - while (++i < CACHE_SIZE && nodes[i]) - if (nodes[i]->idx == idx) - return nodes[index->cacheI = i]; - - if (i < CACHE_SIZE) // Loads the node if there is enough space in the node cache. - { - (node = nodes[index->cacheI = i] = createNode(index))->idx = idx; - nodeLoad(context, node); - return node; - } - - return null; -} - -// juliana@230_21: MAX() and MIN() now use indices on simple queries. -/** - * Finds the minimum value of an index in a range. - * - * @param context The thread context where the function is being executed. - * @param index The index where to find the minimum value. - * @param sqlValue The minimum value inside the given range to be returned. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @return false if an error occurs; true, otherwise. - */ -bool findMinValue(Context context, Index* index, SQLValue* sqlValue, IntVector* bitMap) -{ - TRACE("findMinValue") - Node* curr; - Key* currKeys; - uint16* children; - uint16* vector = (uint16*)index->table->nodes; - int32 size, - idx = 0, - i, - nodeCounter = index->nodeCount + 1, - record; - uint32 count = 1; - - // Recursion using a stack. The array sole element is 0. - vector[0] = 0; - while (count) - { - idx = vector[--count]; - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (!(curr = indexLoadNode(context, index, idx))) - return false; - - // Searches for the smallest key of the node marked in the result set or is not deleted. - size = curr->size; - i = -1; - currKeys = curr->keys; - children = curr->children; - - while (++i < size) - if ((record = currKeys[i].record) != NO_VALUE && (!bitMap || IntVectorisBitSet(bitMap, record))) - { - xmemmove(sqlValue, currKeys[i].keys, sizeof(SQLValue)); - count = 0; // juliana@284_3: solved a possible wrong result in MAX() and MIN() if the column searched had an index. - break; - } - - // Now searches the children nodes whose keys are smaller than the one marked or all of them if no one is marked. - i++; - if (!nodeIsLeaf(curr)) - while (--i >= 0) - vector[count++] = children[i]; - } - - return loadStringForMaxMin(context, index, sqlValue); -} - -/** - * Finds the maximum value of an index in a range. - * - * @param context The thread context where the function is being executed. - * @param index The index where to find the minimum value. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param sqlValue The maximum value inside the given range to be returned. - * @return false if an error occurs; true, otherwise. - */ -bool findMaxValue(Context context, Index* index, SQLValue* sqlValue, IntVector* bitMap) -{ - TRACE("findMaxValue") - Node* curr; - Key* currKeys; - uint16* children; - uint16* vector = (uint16*)index->table->nodes; - int32 size, - idx = 0, - i, - nodeCounter = index->nodeCount + 1, - record; - uint32 count = 1; - - // Recursion using a stack. The array sole element is 0. - vector[0] = 0; - while (count) - { - idx = vector[--count]; - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (!(curr = indexLoadNode(context, index, idx))) - return false; - - // Searches for the smallest key of the node marked in the result set. - i = size = curr->size; - currKeys = curr->keys; - children = curr->children; - - while (--i >= 0) - if ((record = currKeys[i].record) != NO_VALUE && (!bitMap || IntVectorisBitSet(bitMap, record))) - { - xmemmove(sqlValue, currKeys[i].keys, sizeof(SQLValue)); - count = 0; // juliana@284_3: solved a possible wrong result in MAX() and MIN() if the column searched had an index. - break; - } - - // Now searches the children nodes whose keys are smaller than the one marked or all of them if no one is marked. - if (!nodeIsLeaf(curr)) - while (++i <= size) - vector[count++] = children[i]; - } - - return loadStringForMaxMin(context, index, sqlValue); -} - -/** - * Loads a string from the table if needed. - * - * @param context The thread context where the function is being executed. - * @param index The index where to find the minimum value. - * @param sqlValue The record structure which will hold (holds) the string. - * @return false if an error occurs; true, otherwise or no record was found. - */ -bool loadStringForMaxMin(Context context, Index* index, SQLValue* sqlValue) -{ - TRACE("loadStringForMaxMin") - PlainDB* plainDB = &index->table->db; - - if (sqlValue->isNull) // No record found. - return true; - - sqlValue->asBlob = (uint8*)plainDB; - - // If the type is string and the value is not loaded, loads it. - if ((*index->types == CHARS_TYPE || *index->types == CHARS_NOCASE_TYPE) && !sqlValue->length) - { - XFile* dbo = &plainDB->dbo; - int32 length = 0; - nfSetPos(dbo, sqlValue->asInt); // Gets and sets the string position in the .dbo. - - // Fetches the string length. - if (!nfReadBytes(context, dbo, (uint8*)&length, 2) - || !loadString(context, plainDB, sqlValue->asChars, sqlValue->length = length)) - return false; - } - return true; -} - -// juliana@230_29: order by and group by now use indices on simple queries. -/** - * Sorts the records of a table into a temporary table using an index in the ascending order. - * - * @param context The thread context where the function is being executed. - * @param index The index being used to sort the query results. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool sortRecordsAsc(Context context, Index* index, IntVector* bitMap, Table* tempTable, SQLValue** record, int16* columnIndexes, Heap heap) -{ - TRACE("sortRecordsAsc") - int32 size, - i, - valRec, - node = 0, - nodeCounter = index->nodeCount + 1; - uint32 count = 1; - Node* curr; - uint16* nodes = TC_heapAlloc(heap, nodeCounter << 1); - int32* valRecs = index->table->nodes; - Key* keys; - uint16* children; - - // Recursion using a stack. The nodes array sole element is 0. - valRecs[0] = NO_VALUE; - while (count) - { - node = nodes[--count]; // Gets the child node. - valRec = valRecs[count]; // Gets the key node. - - // Loads a node if it is not a leaf node. - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (!(curr = indexLoadNode(context, index, node))) - return false; - - size = curr->size; - children = curr->children; - keys = curr->keys; - - if (nodeIsLeaf(curr)) // If the node do not have children, just process its keys in the ascending order. - { - i = -1; - while (++i < size) - if (!writeKey(context, index, keys[i].record, bitMap, tempTable, record, columnIndexes)) - return false; - if (!writeKey(context, index, valRec, bitMap, tempTable, record, columnIndexes)) - return false; - } - else // If not, push its key and process its children in the ascending order. - { - if (size > 0) - { - valRecs[count] = valRec; - nodes[count++] = children[size]; - } - while (--size >= 0) - { - valRecs[count] = keys[size].record; - nodes[count++] = children[size]; - } - } - } - return true; -} - -/** - * Sorts the records of a table into a temporary table using an index in the descending order. - * - * @param context The thread context where the function is being executed. - * @param index The index being used to sort the query results. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool sortRecordsDesc(Context context, Index* index, IntVector* bitMap, Table* tempTable, SQLValue** record, int16* columnIndexes, Heap heap) -{ - TRACE("sortRecordsDesc") - int32 size, - i, - valRec, - node = 0, - nodeCounter = index->nodeCount + 1; - uint32 count = 1; - Node* curr; - uint16* nodes = TC_heapAlloc(heap, nodeCounter << 1); - int32* valRecs = index->table->nodes; - Key* keys; - uint16* children; - - // Recursion using a stack. The nodes array sole element is 0. - valRecs[0] = NO_VALUE; - while (count) - { - node = nodes[--count]; // Gets the child node. - valRec = valRecs[count]; // Gets the key node. - - // Loads a node if it is not a leaf node. - if (--nodeCounter < 0) // juliana@220_16: does not let the index access enter in an infinite loop. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_LOAD_NODE)); - return false; - } - if (!(curr = indexLoadNode(context, index, node))) - return false; - - size = curr->size; - children = curr->children; - keys = curr->keys; - - if (nodeIsLeaf(curr)) // If the node do not have children, just process its keys in the descending order. - { - if (!writeKey(context, index, valRec, bitMap, tempTable, record, columnIndexes)) - return false; - i = size; - while (--i >= 0) - if (!writeKey(context, index, keys[i].record, bitMap, tempTable, record, columnIndexes)) - return false; - } - else // If not, push its key and process its children in the descending order. - { - i = -1; - while (++i < size) - { - valRecs[count] = keys[i].record; - nodes[count++] = children[i]; - } - if (size > 0) - { - valRecs[count] = valRec; - nodes[count++] = children[size]; - } - } - } - return true; -} - -/** - * Writes all the records with a specific key in the temporary table that satisfy the query where clause. - * - * @param context The thread context where the function is being executed. - * @param index The index being used to sort the query results. - * @param valRec The record index. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @return false if an error occurs; true, otherwise. - */ -bool writeKey(Context context, Index* index, int32 valRec, IntVector* bitMap, Table* tempTable, SQLValue** record, int16* columnIndexes) -{ - TRACE("writeKey") - Table* origTable = index->table; - - if (valRec != NO_VALUE && (!bitMap->items || IntVectorisBitSet(bitMap, valRec))) - { - PlainDB* plainDB = &origTable->db; - uint16* offsets = origTable->columnOffsets; - int8* types = origTable->columnTypes; - uint8* origNulls = origTable->columnNulls; - uint8* tempNulls = tempTable->columnNulls; - uint8* basbuf = plainDB->basbuf; - uint8* buffer = basbuf + offsets[origTable->columnCount]; - - int32 i = tempTable->columnCount, - colIndex, - bytes = NUMBEROFBYTES(origTable->columnCount); - bool isNull; - - if (!plainRead(context, &origTable->db, valRec)) // Reads the record. - return false; - xmemmove(origNulls, buffer, bytes); // Reads the bytes of the nulls. - - while (--i >= 0) // Reads the fields for the temporary table. - { - colIndex = columnIndexes[i]; - if (!(isNull = isBitSet(origNulls, colIndex)) - && !readValue(context, plainDB, record[i], offsets[colIndex], types[colIndex], basbuf, false, false, true, -1, null)) - return false; - setBit(tempNulls, i, isNull); // Sets the null values for tempTable. - } - if (!writeRSRecord(context, tempTable, record)) // Writes the temporary table record. - return false; - } - return true; -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if createComposedIndex() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(createComposedIndex) -{ - Heap heap = heapCreate(); - uint8 columns[256]; - int32 i = 256; - ComposedIndex* compIndex; - UNUSED(currentContext) - - IF_HEAP_ERROR(heap) - { - heapDestroy(heap); - TEST_FAIL(tc, "OutOfMemoryError"); - goto finish; - } - - while (--i >= 0) // Creates composed index structures of various sizes. - { - xmemset(columns, i, i); - ASSERT1_EQUALS(NotNull, compIndex = createComposedIndex(i, columns, i, heap)); - ASSERT2_EQUALS(I32, compIndex->indexId, i); - ASSERT2_EQUALS(I32, compIndex->numberColumns, i); - ASSERT3_EQUALS(Block, columns, compIndex->columns, i); - } - - heapDestroy(heap); - -finish: ; -} -#endif diff --git a/LitebaseSDK/src/native/Index.h b/LitebaseSDK/src/native/Index.h deleted file mode 100644 index 06af9886ef..0000000000 --- a/LitebaseSDK/src/native/Index.h +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * Declares functions to deal a B-Tree header. - */ - -#ifndef LITEBASE_INDEX_H -#define LITEBASE_INDEX_H - -#include "Litebase.h" - -/** - * Creates a composed index. - * - * @param id The index id. - * @param columns The columns of this index. - * @param numberColumns The number of columns of this index. - * @param heap The heap to allocate the compoded index structure. - * @return The composed index. - */ -ComposedIndex* createComposedIndex(int32 id, uint8* columns, int32 numberColumns, Heap heap); - -/** - * Constructs an index structure. - * - * @param context The thread context where the function is being executed. - * @param table The table of the index. - * @param keyTypes The types of the columns of the index. - * @param colSizes The column sizes. - * @param name The name of the index table. - * @param numberColumns The number of columns of the index. - * @param exist Indicates that the index files already exist. - * @param heap A heap to allocate the index structure. - * @return The index created or null if an error occurs. - * @throws DriverException If is not possible to create the index files. - */ -Index* createIndex(Context context, Table* table, int8* keyTypes, int32* colSizes, CharP name, int32 numberColumns, bool exist, Heap heap); - -/** - * Creates an index. - * - * @param context The thread context where the function is being executed. - * @param table The table name whose index is to be created. - * @param columnHashes The hashes of the index columns. - * @param isPKCreation Indicates if the index to be created is the primary key. - * @param indexCount The column numbers of the index. - * @param composedPKCols The columns of the composed primary key. - * @return false if an error occured; true, otherwise. - * @throws DriverException If a column for the index does not exist or is of type blob. - * @throws SQLParseException If a column for the index is of type blob. - */ -bool driverCreateIndex(Context context, Table* table, int32* columnHashes, bool isPKCreation, int32 indexCount, uint8* composedPKCols); - -/** - * Removes a value from the index. - * - * @param context The thread context where the function is being executed. - * @param key The key to be removed. - * @param record The record being removed. - * @return true If the value was removed; false otherwise. - * @throws DriverException If its not possible to find the key record to delete or the index is corrupted. - */ -bool indexRemoveValue(Context context, Key* key, int32 record); - -/** - * Loads a node. - * - * @param context The thread context where the function is being executed. - * @param index The index. - * @param idx The index of the value to be loaded. - * @return The node or null in case of an error. - * @throws DriverException If the index is corrupted. - */ -Node* indexLoadNode(Context context, Index* index, int32 idx); - -/** - * Finds the given key and make the monkey climb on the values. - * - * @param context The thread context where the function is being executed. - * @param key The key to be found. - * @param markBits The rows which will be returned to the result set. - * @return false if an error occured; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool indexGetValue(Context context, Key* key, MarkBits* markBits); - -/** - * Climbs on the nodes that are greater or equal than the current one. - * - * @param context The thread context where the function is being executed. - * @param node The node to be compared with. - * @param start The first key of the node to be searched. - * @param markBits The rows which will be returned to the result set. - * @param stop Indicates when the climb process can be finished. - * @return If it has to stop the climbing process or not, or false if an error occured. - */ -bool indexClimbGreaterOrEqual(Context context, Node* node, int32 start, MarkBits* markBits, bool* stop); - -/** - * Starts from the root to find the left key, then climbs from it until the end. - * - * @param context The thread context where the function is being executed. - * @param left The left key. - * @param markBits The rows which will be returned to the result set. - * @return false if an error occured; true, otherwise. - * @throws DriverException If the index is corrupted. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - */ -bool indexGetGreaterOrEqual(Context context, Key* left, MarkBits* markBits); - -/** - * Splits the overflown node of this B-Tree. The stack ancestors contains all ancestors of the node, together with the known insertion position in - * each of these ancestors. - * - * @param context The thread context where the function is being executed. - * @param curr The current node. - * @param count The number of elements in the ancestors array. - * @return false if an error occured; true, otherwise. - */ -bool indexSplitNode(Context context, Node* curr, int32 count); - - /** - * Removes the index files. - * - * @param context The thread context where the function is being executed. - * @param index The index to be removed. - * @return false if an error occured; true, otherwise. - */ -bool indexRemove(Context context, Index* index); - -/** - * Closes the index files. - * - * @param context The thread context where the function is being executed. - * @param index The index to be removed. - * @return false if an error occured; true, otherwise. - */ -bool indexClose(Context context, Index* index); - -/** - * Empties the index files, since the rows were deleted. - * - * @param context The thread context where the function is being executed. - * @param index The index to be erased. - * @return false if an error occured; true, otherwise. - * @throws DriverException If it is not possible to truncate the index files. - */ -bool indexDeleteAllRows(Context context, Index* index); - -/** - * Delays the write to disk, caching them at memory. - * - * @param context The thread context where the function is being executed. - * @param index The index. - * @param delayed Indicates if the writing process is to be done later or not. - * @return false if an error occured; true, otherwise. - */ -bool indexSetWriteDelayed(Context context, Index* index, bool delayed); - -/** - * Adds a key to an index. - * - * @param context The thread context where the function is being executed. - * @param index The index where the key is going to be inserted. - * @param values The key to be inserted. - * @param record The record of the key in the table. - * @return false if an error occured; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool indexAddKey(Context context, Index* index, SQLValue** values, int32 record); - -/** - * Renames the index files. - * - * @param context The thread context where the function is being executed. - * @param index The index which will be renamed. - * @param newName The new name for the index. - * @return false if an error occured; true, otherwise. - */ -bool indexRename(Context context, Index* index, CharP newName); - -/** - * Finds the minimum value of an index in a range. - * - * @param context The thread context where the function is being executed. - * @param index The index where to find the minimum value. - * @param sqlValue The minimum value inside the given range to be returned. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @return false if an error occurs; true, otherwise. - */ -bool findMinValue(Context context, Index* index, SQLValue* sqlValue, IntVector* bitMap); - -/** - * Finds the maximum value of an index in a range. - * - * @param context The thread context where the function is being executed. - * @param index The index where to find the minimum value. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param sqlValue The maximum value inside the given range to be returned. - * @return false if an error occurs; true, otherwise. - */ -bool findMaxValue(Context context, Index* index, SQLValue* sqlValue, IntVector* bitMap); - -/** - * Loads a string from the table if needed. - * - * @param context The thread context where the function is being executed. - * @param index The index where to find the minimum value. - * @param sqlValue The record structure which will hold (holds) the string. - * @return false if an error occurs; true, otherwise or no record was found. - */ -bool loadStringForMaxMin(Context context, Index* index, SQLValue* sqlValue); - -/** - * Returns a node already loaded or loads it if there is empty space in the cache node to avoid loading already loaded nodes. - * - * @param context The thread context where the function is being executed. - * @param index The index where a node is going to be fetched. - * @return The loaded node, a new cache node with the requested node loaded, a first level node, or null if it is not - * already loaded and its cache is full. - */ -Node* getLoadedNode(Context context, Index* index, int32 idx); - -/** - * Sorts the records of a table into a temporary table using an index in the ascending order. - * - * @param context The thread context where the function is being executed. - * @param index The index being used to sort the query results. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool sortRecordsAsc(Context context, Index* index, IntVector* bitMap, Table* tempTable, SQLValue** record, int16* columnIndexes, Heap heap); - -/** - * Sorts the records of a table into a temporary table using an index in the descending order. - * - * @param context The thread context where the function is being executed. - * @param index The index being used to sort the query results. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the index is corrupted. - */ -bool sortRecordsDesc(Context context, Index* index, IntVector* bitMap, Table* tempTable, SQLValue** record, int16* columnIndexes, Heap heap); - -/** - * Writes all the records with a specific key in the temporary table that satisfy the query where clause. - * - * @param context The thread context where the function is being executed. - * @param index The index being used to sort the query results. - * @param valRec The record index. - * @param bitMap The table bitmap which indicates which rows will be in the result set. - * @param tempTable The temporary table for the result set. - * @param record A record for writing in the temporary table. - * @param columnIndexes Has the indices of the tables for each resulting column. - * @return false if an error occurs; true, otherwise. - */ -bool writeKey(Context context, Index* index, int32 valRec, IntVector* bitMap, Table* tempTable, SQLValue** record, int16* columnIndexes); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if createComposedIndex() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_createComposedIndex(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/Key.c b/LitebaseSDK/src/native/Key.c deleted file mode 100644 index 8d677c9aee..0000000000 --- a/LitebaseSDK/src/native/Key.c +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * Defines functions to deal with the key of a record. It may be any of the SQL types. - */ - -#include "Key.h" - -/** - * Sets a key of an index. - * - * @param key Yhe the index key to be set. - * @param SQLValues The values used to set the index key. - * @param index The index. - * @param size The number of columns of the index. - */ -void keySet(Key* key, SQLValue** SQLValues, Index* index, int32 size) -{ - TRACE("keySet") - key->index = index; - - // juliana@202_3: Solved a bug that could cause a GPF when using composed indices. - while (--size >= 0) - key->keys[size] = *SQLValues[size]; - key->record = NO_VALUE; // The record key is not stored yet. -} - -/** - * Sets a key using another key. - * - * @param to The destination key. - * @param from The key used to set the other key. - */ -void keySetFromKey(Key* to, Key* from) -{ - TRACE("keySetFromKey") - Index* index = to->index; - SQLValue* toKeys = to->keys; - SQLValue* fromKeys = from->keys; - SQLValue* fromKey; - SQLValue* toKey; - int32* sizes = index->colSizes; - int32 i = index->numberColumns; - - while (--i >= 0) - { - fromKey = &fromKeys[i]; - toKey = &toKeys[i]; - if (!sizes[i]) - xmemmove(toKey, fromKey, sizeof(SQLValue)); // full copy - else - { - // juliana@202_8: corrected a bug that would cause string indices to be built incorrectly if they had more than one null value. - if (fromKey->asChars) - xmemmove(toKey->asChars, fromKey->asChars, (toKey->length = fromKey->length) << 1); - else - *toKey->asChars = toKey->length = 0; - - toKey->asInt = fromKey->asInt; - } - } - to->index = from->index; - to->record = from->record; -} - -/** - * Loads a key. - * - * @param key The key to be loaded. - * @param dataStream The data stream where the record to be read to find the key value stored. - * @return The dataStream offset of the number of bytes read. - */ -uint8* keyLoad(Key* key, uint8* dataStream) -{ - TRACE("keyLoad") - Index* index = key->index; - PlainDB* plainDB = &index->table->db; - SQLValue* keys = key->keys; - SQLValue* keyAux; - int8* types = index->types; - int32* sizes = index->colSizes; - int32 i = -1, - n = index->numberColumns, - pos; - - while (++i < n) - { - keyAux = &keys[i]; - if (sizes[i]) // String keys are not stored in the indices. Only their pointer is stored. - { - xmove4(&pos, dataStream); - dataStream += 4; - if (pos != keyAux->asInt) // If the position is the same, the string is already loaded. - { - keyAux->asInt = pos; - keyAux->length = 0; - keyAux->asChars[0] = 0; - } - } - else - { - // juliana@230_12 - // Must pass true to isTemporary so that the method does not think that the number is a rowid. - // If the value read is null, some bytes must be skipped in the stream. - // Note: since we're writing only primitive types, we can use any PlainDB available. - readValue(null, plainDB, keyAux, 0, types[i], dataStream, true, false, false, -1, null); - dataStream += typeSizes[types[i]]; - } - } - xmove4(&key->record, dataStream); // Reads the number that represents the record. - return dataStream + 4; -} - -/** - * Saves a key. - * - * @param key The key to be saved. - * @param dataStream The data stream where the record to be read to find the key value stored. - * @return The dataStream offset of the number of bytes written. - */ -uint8* keySave(Key* key, uint8* dataStream) -{ - TRACE("keySave") - Index* index = key->index; - SQLValue* keys = key->keys; - int8* types = index->types; - int32* sizes = index->colSizes; - int32 i = -1, - n = index->numberColumns; - - while (++i < n) - { - if (sizes[i]) - { - xmove4(dataStream, &keys[i].asInt); // Saves only the string position in the .dbo. - dataStream += 4; - } - else - { - // If the key is not a string, stores its value in the index file. - // Note: since primitive types are being written, it is possible to use any PlainDB available. - writeValue(null, null, &keys[i], dataStream, types[i], 0, true, true, false, false); - dataStream += typeSizes[types[i]]; - } - } - - xmove4(dataStream, &key->record); // Writes the number that represents the record. - return dataStream + 4; -} - -/** - * Compares two keys. - * - * @param context The thread context where the function is being executed. - * @param key1 The first key to be compared. - * @param key2 The second key to be compared. - * @param isNull1 Indicates if the fist key is null. - * @param plainDB the plainDB of a table if it is necessary to load a string. - * @return 0 if the keys are identical; a positive number if key1 keys are greater than key2 keys; otherwise, a negative - * number. - */ -int32 keyCompareTo(Context context, Key* key1, Key* key2, int32 size, PlainDB* plainDB) -{ - TRACE("keyCompareTo") - int32 r, - i = -1; - int8* types = key1->index->types; - SQLValue* keys1 = key1->keys; - SQLValue* keys2 = key2->keys; - - while (++i < size) // Compares each key of the key. If a pair is not equal to each other, returns. - if ((r = valueCompareTo(context, &keys1[i], &keys2[i], types[i], false, false, plainDB)) != 0) - return r; - - return 0; -} diff --git a/LitebaseSDK/src/native/Key.h b/LitebaseSDK/src/native/Key.h deleted file mode 100644 index e62fefafb4..0000000000 --- a/LitebaseSDK/src/native/Key.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * Declares functions to deal with the key of a record. It may be any of the SQL types. - */ - -#ifndef LITEBASE_KEY_H -#define LITEBASE_KEY_H - -#include "Litebase.h" -#include "LitebaseTypes.h" - -/** - * Sets a key of an index. - * - * @param key Yhe the index key to be set. - * @param SQLValues The values used to set the index key. - * @param index The index. - * @param size The number of columns of the index. - */ -void keySet(Key* key, SQLValue** SQLValues, Index* index, int32 size); - -/** - * Sets a key using another key. - * - * @param to The destination key. - * @param from The key used to set the other key. - */ -void keySetFromKey(Key* to, Key* from); - -/** - * Loads a key. - * - * @param key The key to be loaded. - * @param dataStream The data stream where the record to be read to find the key value stored. - * @return The dataStream offset of the number of bytes read. - */ -uint8* keyLoad(Key* key, uint8* dataStream); - -/** - * Saves a key. - * - * @param key The key to be saved. - * @param dataStream The data stream where the record to be read to find the key value stored. - * @return The dataStream offset of the number of bytes written. - */ -uint8* keySave(Key* key, uint8* dataStream); - -/** - * Compares two keys. - * - * @param context The thread context where the function is being executed. - * @param key1 The first key to be compared. - * @param key2 The second key to be compared. - * @param isNull1 Indicates if the fist key is null. - * @param plainDB the plainDB of a table if it is necessary to load a string. - * @return 0 if the keys are identical; a positive number if key1 keys are greater than key2 keys; otherwise, a negative - * number. - */ -int32 keyCompareTo(Context context, Key* key1, Key* key2, int32 size, PlainDB* plainDB); - -#endif diff --git a/LitebaseSDK/src/native/Litebase.c b/LitebaseSDK/src/native/Litebase.c deleted file mode 100644 index da5f7b8046..0000000000 --- a/LitebaseSDK/src/native/Litebase.c +++ /dev/null @@ -1,3130 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines functions to deal with important Litebase funcionalities. - */ - -#include "Litebase.h" - -#ifdef ENABLE_TEST_SUITE // Enable internal test cases running. -bool ranTests; -#endif - -/** - * A list of objects used to hold prepared statements that uses a specific table. - */ -TC_ImplementList(TCObject); - -/** - * Loads the necessary data when using Litebase for the first time. - * - * @param params Some parameters and function pointers in order to load a .dll. - * @return false if an error occurs; true, otherwise. - */ -LB_API bool LibOpen(OpenParams params) -{ - if (!initVars(params)) // Initializes Litebase structures. - return false; -#ifdef ENABLE_TEST_SUITE // Runs internal test cases. - if (!ranTests) - { - TestSuite testSuite; - Context currentContext = params->currentContext; - - ranTests = true; - - // Initializes the used test functions. - testSuite.assertEqualsI16 = assertEqualsI16; - testSuite.assertEqualsI32 = assertEqualsI32; - testSuite.assertEqualsI64 = assertEqualsI64; - testSuite.assertEqualsU8 = assertEqualsU8; - testSuite.assertEqualsDbl = assertEqualsDbl; - testSuite.assertEqualsSz = assertEqualsSz; - testSuite.assertEqualsPtr = assertEqualsPtr; - testSuite.assertEqualsBlock = assertEqualsBlock; - testSuite.assertEqualsNull = assertEqualsNull; - testSuite.assertEqualsNotNull = assertEqualsNotNull; - testSuite.assertEqualsTrue = assertEqualsTrue; - testSuite.assertEqualsFalse = assertEqualsFalse; - testSuite.fail = fail; - testSuite.doubleError = 1e-8; - testSuite.failed = 0; - - // The test cases. - test_createComposedIndex(&testSuite, currentContext); - test_initLex(&testSuite, currentContext); - test_getMessage(&testSuite, currentContext); - test_initLitebaseMessage(&testSuite, currentContext); - test_initLitebaseParser(&testSuite, currentContext); - test_lbError(&testSuite, currentContext); - test_lbErrorWithMessage(&testSuite, currentContext); - test_LibClose(&testSuite, currentContext); - test_LibOpen(&testSuite, currentContext); - test_bindFunctionDataType(&testSuite, currentContext); - test_checkApppath(&testSuite, currentContext); - test_dataTypeFunctionsName(&testSuite, currentContext); - test_initVars(&testSuite, currentContext); - test_markBitsOnValue(&testSuite, currentContext); - test_markBitsReset(&testSuite, currentContext); - test_mfClose(&testSuite, currentContext); - test_mfGrowTo(&testSuite, currentContext); - test_mfReadBytes(&testSuite, currentContext); - test_mfSetPos(&testSuite, currentContext); - test_mfWriteBytes(&testSuite, currentContext); - test_applyDataTypeFunction(&testSuite, currentContext); - test_newSQLValues(&testSuite, currentContext); - test_valueCompareTo(&testSuite, currentContext); - test_initTCVMLib(&testSuite, currentContext); - test_rowUpdated(&testSuite, currentContext); - currentContext->thrownException = null; - - // The test results. - TC_alert("%02d test total\n%02d succeeded\n%02d failed", 30, 30 - testSuite.failed, testSuite.failed); - } -#endif - return true; -} - -/** - * Flushs all pending data and destroy all Litebase structures when closing the application. - */ -LB_API void LibClose() -{ - TRACE("LibClose") - getMainContextFunc TC_getMainContext = TC_getProcAddress(null, "getMainContext"); - - TC_htFreeContext(TC_getMainContext(), &htCreatedDrivers, (VisitElementContextFunc)freeLitebase); // Flushs pending data and closes all tables. - muFree(&memoryUsage); // Destroys memory usage hash table. - TC_htFree(&reserved, null); // Destroys the reserved words hash table. - - // Destroy the mutexes. - DESTROY_MUTEX(parser); - DESTROY_MUTEX(log); -} - -/** - * Loads the necessary data when using Litebase for the first time. - * - * @param params Some parameters and function pointers in order to load a .dll. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError if a memory allocation fails. - */ -bool initVars(OpenParams params) -{ - Context context = params->currentContext; - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Initializes the list of Litebase opened files. -#if defined(POSIX) || defined(ANDROID) - xmemzero(&filesList, sizeof(XFilesList)); -#endif - - // Initializes the mutexes. - SETUP_MUTEX; - INIT_MUTEX(parser); - INIT_MUTEX(log); - INIT_MUTEX(files); - - memoryUsage.items = null; - reserved.items = null; - - // Initializes the TCVM functions needed by Litebase. - TC_getProcAddress = (getProcAddressFunc)params->getProcAddress; - initTCVMLib(); - - if (!(htCreatedDrivers = TC_htNew(10, null)).items // Allocates a hash table for the loaded connections. - || !(memoryUsage = muNew(100)).items // Allocates a hash table for select statistics. - || !initLex()) // Initializes the lex structures. - { - TC_htFree(&htCreatedDrivers, null); - TC_htFree(&reserved, null); - muFree(&memoryUsage); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - initLitebaseMessage(); // Loads Litebase error messages. - - make_crc_table(); // Initializes the crc table for calculating crc32 codes. - - // Loads classes. - litebaseConnectionClass = TC_loadClass(context, "litebase.LitebaseConnection", false); - loggerClass = TC_loadClass(context, "totalcross.util.Logger", false); - - return true; -} - -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Creates a LitebaseConnection for the given creator id and with the given connection param list. This method avoids the creation of more than - * one instance with the same creator id and parameters, which would lead to performance and memory problems. - * - * @param context The thread context where the function is being executed. - * @param crid The creator id, which may be the same one of the current application and MUST be 4 characters long. - * @param objParams Only the folder where it is desired to store the tables, null, if it is desired to use the current data - * path, or chars_type = chars_format; path = source_path[;crypto] , where chars_format can be ascii or - * unicode, source_path is the folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is passed as a parameter, unicode is used and there is no - * cryptography. Notice that path must be absolute, not relative. - *

    Note that databases belonging to multiple applications can be stored in the same path, since all tables are prefixed by the application's - * creator id. - *

    Also notice that to store Litebase files on card on Pocket PC, just set the second parameter to the correct directory path. - *

    It is not recommended to create the databases directly on the PDA. Memory cards are FIVE TIMES SLOWER than the main memory, so it will take - * a long time to create the tables. Even if the NVFS volume is used, it can be very slow. It is better to create the tables on the desktop, and - * copy everything to the memory card or to the NVFS volume. - *

    Due to the slowness of a memory card and the NVFS volume, all queries will be stored in the main memory; only tables and indexes will be - * stored on the card or on the NVFS volume. - *

    An exception will be raised if tables created with an ascii kind of connection are oppened with an unicode connection and vice-versa. - * @return A Litebase instance. - * @throws OutOfMemoryError If memory allocation fails. - */ -TCObject create(Context context, int32 crid, TCObject objParams) -{ - TRACE("create") - TCObject driver, - logger = litebaseConnectionClass->objStaticValues[1]; - int32 hash; - bool isAscii = false, - useCrypto = false; - TCHAR sourcePath[1024]; - TCHARP path = null; - char params[1024]; - - if (logger) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - char cridBuffer[5]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - TC_int2CRID(crid, cridBuffer); - if (!TC_appendCharP(context, logSBuffer, "new LitebaseConnection(") || !TC_appendCharP(context, logSBuffer, cridBuffer) - || !TC_appendCharP(context, logSBuffer, ",")) - goto error; - - if (objParams) - { - if (!TC_appendJCharP(context, logSBuffer, String_charsStart(objParams), String_charsLen(objParams))) - goto error; - } - else if (!TC_appendCharP(context, logSBuffer, "null")) - goto error; - if (!TC_appendCharP(context, logSBuffer, ")")) - goto error; - - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - -error: - UNLOCKVAR(log); - if (context->thrownException) // juliana@223_14: solved possible memory problems. - return null; - } - - if (objParams) - { - CharP tempParams[3]; - int32 i = 1, - numParams; - - params[0] = 0; - tempParams[0] = tempParams[1] = tempParams[2] = null; - - // juliana@250_4: now getInstance() can receive only the parameter chars_type = ... - // juliana@210_2: now Litebase supports tables with ascii strings. - TC_JCharP2CharPBuf(String_charsStart(objParams), String_charsLen(objParams), params); - tempParams[0] = params; - tempParams[1] = xstrchr(params, ';'); // Separates the parameters. - if (tempParams[1]) - { - tempParams[1][0] = 0; - tempParams[2] = xstrchr(++tempParams[1], ';'); - i++; - - if (tempParams[2]) - { - tempParams[2][0] = 0; - tempParams[2]++; - i++; - } - } - - numParams = i; - while (--i >= 0) // The parameters order does not matter. - { - tempParams[i] = strTrim(tempParams[i]); - if (xstrstr(tempParams[i], "chars_type")) // Chars type param. - isAscii = (xstrstr(tempParams[i], "ascii") != null); - else if (xstrstr(tempParams[i], "path")) // Path param. - path = TC_CharP2TCHARPBuf(&xstrchr(tempParams[i], '=')[1], sourcePath); - else if (xstrstr(tempParams[i], "crypto")) // Cryptography param. - useCrypto = true; - else if (numParams == 1) - path = TC_CharP2TCHARPBuf(tempParams[0], sourcePath); // Things do not change if there is only one parameter. - else // juliana@253_11: now a DriverException will be throw if an incorrect parameter is passed in LitebaseConnection.getInstance(). - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PARAMETER), tempParams[i]); - return null; - } - } - } - - // Gets the slot and checks the path validity. - if (!checkApppath(context, sourcePath, path)) // juliana@214_1 - return null; - - // juliana@221_3: solved a small bug that could make Litebase crash on Windows 32, Windows CE, Palm, iPhone, and Android when passing 2 arguments to - // params when issuing a LitebaseConnection.getInstance(). - - // fdie@555_2: driver not already created? Creates one. - // If there is no connections with this key, creates a new one. - if (!(driver = TC_htGetPtr(&htCreatedDrivers, (hash = TC_hashCodeFmt("ixiis", crid, context->thread, isAscii, useCrypto, sourcePath? TC_TCHARP2CharPBuf(sourcePath, params): "null"))))) - { - Hashtable htTables, - htPS; - - if (!(driver = TC_createObject(context, "litebase.LitebaseConnection"))) - return null; - - OBJ_LitebaseAppCrid(driver) = crid; // juliana@210a_10 - OBJ_LitebaseIsAscii(driver) = isAscii; - OBJ_LitebaseUseCrypto(driver) = useCrypto; - OBJ_LitebaseKey(driver) = hash; - - // SourcePath. - if (!(setLitebaseSourcePath(driver, xmalloc(sizeof(TCHAR) * (tcslen(sourcePath) + 1))))) - { -error1: - freeLitebase(context, (size_t)driver); - TC_setObjectLock(driver, UNLOCKED); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - tcscpy(getLitebaseSourcePath(driver), sourcePath); - - // Current loaded tables. - if (!(htTables = TC_htNew(10, null)).items) - goto error1; - if (!(setLitebaseHtTables(driver, xmalloc(sizeof(Hashtable))))) - goto error1; - xmemmove(getLitebaseHtTables(driver), &htTables, sizeof(Hashtable)); - - // Current loaded prepared statements. - // juliana@226_16: prepared statement is now a singleton. - if (!(htPS = TC_htNew(30, null)).items) - goto error1; - if (!(setLitebaseHtPS(driver, xmalloc(sizeof(Hashtable))))) - goto error1; - xmemmove(getLitebaseHtPS(driver), &htPS, sizeof(Hashtable)); - - // juliana@253_6: the maximum number of keys of a index was duplicated. - if (!setLitebaseNodes(driver, xmalloc(MAX_IDX << 2))) - goto error1; - - // Stores the driver into the drivers hash table. - if (!TC_htPutPtr(&htCreatedDrivers, hash, driver)) - goto error1; - } - else - TC_setObjectLock(driver, LOCKED); - return driver; -} - -/** - * Frees all data concerning a certaim driver connection. - * - * @param context The thread context where the function is being executed. - * @param driver The driver as an int because this function may be called from a hash table. - */ -void freeLitebase(Context context, size_t driver) -{ - TRACE("freeLitebase") - TCHARP sourcePath = getLitebaseSourcePath(driver); - int32* nodes = getLitebaseNodes(driver); // juliana@253_6: the maximum number of keys of a index was duplicated. - Hashtable* htTables = getLitebaseHtTables(driver); - Hashtable* htPs = getLitebaseHtPS(driver); - - if (htTables) // Frees all the openned tables and the their hash table. - { - TC_htFreeContext(context, htTables, (VisitElementContextFunc)freeTableHT); - xfree(htTables); - } - - if (htPs) // juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - { - TC_htFree(htPs, (VisitElementFunc)freePreparedStatement); - xfree(htPs); - } - - xfree(sourcePath); // Frees the source path. - xfree(nodes); // juliana@253_6: the maximum number of keys of a index was duplicated. - TC_htRemove(&htCreatedDrivers, OBJ_LitebaseKey((TCObject)driver)); // fdie@555_2: removes this instance from the drivers hash table. - OBJ_LitebaseDontFinalize((TCObject)driver) = true; // This object shouldn't be finalized again. -} - -/** - * Used to execute a create table or create index SQL commands. - * - *

    Examples: - *

      - *
    • driver.execute("create table PERSON (NAME CHAR(30), SALARY DOUBLE, AGE INT, EMAIL CHAR(50))"); - *
    • driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - *
    - * - *

    When creating an index, its name is ignored but must be given. The index can be created after data was added to the table. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param sqlStr The SQL creation command. - * @param sqlLen The SQL string length. - * @throws SQLParseException If the table name or a default string is too big, there is an invalid default value, or an unknown (on a create table) - * or repeated column name, or an invalid number occurs. - * @throws AlreadyCreatedException If the table or index is already created. - * @throws OutOfMemoryError If a memory allocation fails. - */ -void litebaseExecute(Context context, TCObject driver, JCharP sqlStr, uint32 sqlLen) -{ - TRACE("litebaseExecute") - char tableName[DBNAME_SIZE]; - LitebaseParser* parser; - bool locked = true; - int32 i; - int32* hashes; - CharP* names; - Heap heapParser = heapCreate(), - heap = null; - - // Does de parsing. - LOCKVAR(parser); - IF_HEAP_ERROR(heapParser) - { - if (locked) - UNLOCKVAR(parser); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); -error: - heapDestroy(heapParser); - heapDestroy(heap); - return; - } - heapParser->greedyAlloc = true; - parser = initLitebaseParser(context, sqlStr, sqlLen, false, heapParser); - UNLOCKVAR(parser); - locked = false; - if (!parser) - goto error; - - if (parser->command == CMD_CREATE_TABLE) - { - int32 count = parser->fieldListSize + 1, // fieldListSize + rowid - primaryKeyCol = NO_PRIMARY_KEY, // juliana@114_9 - composedPK = NO_PRIMARY_KEY, - numberComposedPKCols, - type; - bool error = false; - uint8* columnAttrs; - uint8* composedPKCols = null; - int8* types; - int32* sizes; - CharP buffer; - JCharP defaultValue; - SQLValue** defaultValues; - SQLValue* defaultValueI; - SQLFieldDefinition** fieldList = parser->fieldList; - SQLFieldDefinition* field; - DoubleBuf doubleBuf; - - // Verifies the length of the table name. - if (xstrlen(parser->tableList[0]->tableName) > MAX_TABLE_NAME_LENGTH) // rnovais@112_3 rnovais@570_114: The table name can't be infinite. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - goto error; // juliana@201_25: memory leak. - } - - xstrcpy(tableName, parser->tableList[0]->tableName); - if (tableExistsByName(context, driver, tableName)) // guich@105: verifies if it is already created. - goto error; // juliana@201_25: memory leak. - - // Creates the table heap. - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - heap->greedyAlloc = true; - - // Now gets the columns. - hashes = (int32*)TC_heapAlloc(heap, count << 2); - types = (int8*)TC_heapAlloc(heap, count); - sizes = (int32*)TC_heapAlloc(heap, count << 2); - names = (CharP*)TC_heapAlloc(heap, count * TSIZE); - defaultValues = (SQLValue**)TC_heapAlloc(heap, count * TSIZE); - columnAttrs = (uint8*)TC_heapAlloc(heap, count); - - // Creates column 0 (rowid). - hashes[0] = HCROWID; - types[0] = INT_TYPE; - names[0] = "rowid"; - - i = count; - while (--i) // Creates the other columns - { - TC_CharPToLower(buffer = (field = fieldList[i - 1])->fieldName); - hashes[i] = TC_hashCode(names[i] = TC_hstrdup(buffer, heap)); - types[i] = field->fieldType; - sizes[i] = field->fieldSize; - - if (field->isPrimaryKey) // Checks if there is a primary key definition. - primaryKeyCol = i; // juliana@114_9 - - if (field->defaultValue) // Default values: default null has no effect. This is handled by the parser. - { - defaultValueI = defaultValues[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - sqlLen = TC_JCharPLen(defaultValue = field->defaultValue); - columnAttrs[i] |= ATTR_COLUMN_HAS_DEFAULT; // Sets the bit of default. - - if (((type = types[i]) != CHARS_TYPE && type != CHARS_NOCASE_TYPE && sqlLen > 39) - || ((type == CHARS_TYPE || type == CHARS_NOCASE_TYPE) && (int32)sqlLen > sizes[i])) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER)); - goto error; - } - - switch (types[i]) - { - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - defaultValueI->length = sqlLen; - defaultValueI->asChars = TC_heapAlloc(heap, (sqlLen << 1) + 2); - xmemmove(defaultValueI->asChars, defaultValue, sqlLen << 1); - break; - - case SHORT_TYPE: - defaultValueI->asShort = str2short(TC_JCharP2CharPBuf(defaultValue, sqlLen, doubleBuf), &error); - break; - - case INT_TYPE: - defaultValueI->asInt = TC_str2int(TC_JCharP2CharPBuf(defaultValue, sqlLen, doubleBuf), &error); - break; - - case LONG_TYPE: - defaultValueI->asLong = TC_str2long(TC_JCharP2CharPBuf(defaultValue, sqlLen, doubleBuf), &error); - break; - - case FLOAT_TYPE: - defaultValueI->asFloat = str2float(TC_JCharP2CharPBuf(defaultValue, sqlLen, doubleBuf), &error); - break; - - case DOUBLE_TYPE: - defaultValueI->asDouble = TC_str2double(TC_JCharP2CharPBuf(defaultValue, sqlLen, doubleBuf), &error); - break; - - case DATE_TYPE: - case DATETIME_TYPE: - TC_JCharP2CharPBuf(defaultValue, sqlLen, doubleBuf); - if (!testAndPrepareDateAndTime(context, defaultValues[i], doubleBuf, type)) - goto error; - } - - if (error) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), defaultValue, "number"); - goto error; - } - } - - if (field->isNotNull) // Sets the 'not null' bit. - columnAttrs[i] |= ATTR_COLUMN_IS_NOT_NULL; - } - - if ((numberComposedPKCols = parser->fieldNamesSize) > 0) // Gets the composed primary keys. - { - int32 pos = -1, - j, - hashCol; - CharP* fields = parser->fieldNames; - - composedPKCols = (uint8*)TC_heapAlloc(heap, numberComposedPKCols); - i = -1; - - while (++i < numberComposedPKCols) - { - hashCol = TC_hashCode(fields[i]); - j = count; - - while (--j >= 0) // Checks if the name of a table column exist. - if (hashCol == hashes[j]) - { - pos = j; - break; - } - if (pos == -1) // Column not found. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), fields[i]); - goto error; - } - if (types[pos] == BLOB_TYPE) // A blob can't be in a composed PK. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_BLOB_PRIMARY_KEY)); - goto error; - } - j = -1; - while (++j < i) // Verifies if there's a duplicate definition. - if (composedPKCols[j] == pos) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DUPLICATED_COLUMN_NAME), fields[i]); - goto error; - } - composedPKCols[i] = pos; - } - if (numberComposedPKCols == 1) - { - numberComposedPKCols = 0; - primaryKeyCol = pos; - } - else - composedPK = 0; - } - - driverCreateTable(context, driver, tableName, names, hashes, types, sizes, columnAttrs, defaultValues, primaryKeyCol, composedPK, composedPKCols, numberComposedPKCols, count, heap); - } - else if (parser->command == CMD_CREATE_INDEX) - { - Table* table; - - xstrcpy(tableName, parser->tableList[0]->tableName); // indexTableName ignored - formed internally. - table = getTable(context, driver, tableName); - - if (table) - { - Hashtable htTable = TC_htNew((i = parser->fieldNamesSize) + 1, heapParser); - - IF_HEAP_ERROR(table->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - names = parser->fieldNames; - hashes = (int32*)TC_heapAlloc(table->heap, i << 2); - while (--i >= 0) - { - // juliana@225_8: it was possible to create a composed index with duplicated column names. - if (TC_htGet32(&htTable, hashes[i] = TC_hashCode(names[i]))) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DUPLICATED_COLUMN_NAME), names[i]); - goto error; - } - TC_htPut32(&htTable, hashes[i], hashes[i]); - } - driverCreateIndex(context, table, hashes, 0, parser->fieldNamesSize, null); - } - } - else - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED)); - heapDestroy(heapParser); -} - -/** - * Used to execute updates in a table (insert, delete, update, alter table, drop). E.g.: - * - *

    driver.executeUpdate("drop table person"); will drop also the indices. - *

    driver.executeUpdate("drop index * on person"); will drop all indices but not the primary key index. - *

    driver.executeUpdate("drop index name on person"); will drop the index for the "name" column. - *

    driver.executeUpdate("ALTER TABLE person DROP primary key"); will drop the primary key. - *

    driver.executeUpdate("update person set age=44, salary=3200.5 where name = 'guilherme campos hazan'"); - * will update the table. - *

    driver.executeUpdate("delete person where name like 'g%'"); will delete records of the table. - *

    driver.executeUpdate("insert into person (age, salary, name, email) - * values (32, 2000, 'guilherme campos hazan', 'guich@superwaba.com.br')"); will insert a record in the table. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param sqlStr The SQL creation command. - * @param sqlLen The SQL string length. - * @return The number of rows affected or 0 if a drop or alter operation was successful. - * @throws OutOfMemoryError If a memory allocation fails. - */ -int32 litebaseExecuteUpdate(Context context, TCObject driver, JCharP sqlStr, int32 sqlLen) -{ - TRACE("litebaseExecuteUpdate") - LitebaseParser* parser; - int32 returnVal = -1; - Heap heapParser = heapCreate(); - bool locked = true; - - // Does de parsing. - LOCKVAR(parser); - IF_HEAP_ERROR(heapParser) - { - if (locked) - UNLOCKVAR(parser); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto finish; - } - heapParser->greedyAlloc = true; - parser = initLitebaseParser(context, sqlStr, sqlLen, false, heapParser); - UNLOCKVAR(parser); - locked = false; - if (!parser) - goto finish; - - switch (parser->command) - { - case CMD_DROP_TABLE: - litebaseExecuteDropTable(context, driver, parser); - return 0; - case CMD_DROP_INDEX: - return litebaseExecuteDropIndex(context, driver, parser); - case CMD_ALTER_DROP_PK: - case CMD_ALTER_ADD_PK: - case CMD_ALTER_RENAME_TABLE: - case CMD_ALTER_RENAME_COLUMN: - case CMD_ALTER_ADD_COLUMN: - litebaseExecuteAlter(context, driver, parser); - return 0; - case CMD_INSERT: - { - SQLInsertStatement* insertStmt = initSQLInsertStatement(context, driver, parser); - - if (insertStmt && litebaseBindInsertStatement(context, insertStmt)) - returnVal = litebaseDoInsert(context, insertStmt); - goto finish; - - } - case CMD_UPDATE: - { - SQLUpdateStatement* updateStmt = initSQLUpdateStatement(context, driver, parser, false); - - if (updateStmt && litebaseBindUpdateStatement(context, updateStmt)) - returnVal = litebaseDoUpdate(context, updateStmt); - goto finish; - } - case CMD_DELETE: - { - SQLDeleteStatement* deleteStmt = initSQLDeleteStatement(parser, false); - if (litebaseBindDeleteStatement(context, driver, deleteStmt)) - returnVal = litebaseDoDelete(context, deleteStmt); - goto finish; - } - } - -finish: - heapDestroy(heapParser); - return returnVal; -} - -/** - * Used to execute queries in a table. Example: - * - *

    - * ResultSet rs = driver.executeQuery("select rowid, name, salary, age from person where age != 44");
    - * rs.afterLast();
    - * while (rs.prev())
    - *    Vm.debug(rs.getString(1) + ". " + rs.getString(2) + " - " + rs.getInt("age") + " years");
    - * 
    - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param sqlStr The SQL creation command. - * @param sqlLen The SQL string length. - * @return A result set with the values returned from the query or null if an error occurs. - * @throws OutOfMemoryError If a memory allocation fails. - */ -TCObject litebaseExecuteQuery(Context context, TCObject driver, JCharP strSql, int32 length) -{ - TRACE("litebaseExecuteQuery") - Heap heapParser = heapCreate(); - LitebaseParser* parser; - SQLSelectStatement* selectStmt; - ResultSet* resultSetBag; - TCObject resultSet; - PlainDB* plainDB; - bool locked = false; - - // Does the parsing. - IF_HEAP_ERROR(heapParser) - { -nomem: - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); -error: - if (locked) - UNLOCKVAR(parser); - heapDestroy(heapParser); - return null; - } - locked = true; - LOCKVAR(parser); - heapParser->greedyAlloc = true; - parser = initLitebaseParser(context, strSql, length, true, heapParser); - UNLOCKVAR(parser); - locked = false; - if (!parser) - goto error; - - // Creates the select statement, binds it and performs the select. - if (!(selectStmt = initSQLSelectStatement(parser, false)) - || !litebaseBindSelectStatement(context, driver, selectStmt) - || !(resultSet = litebaseDoSelect(context, driver, selectStmt))) - goto error; - - // juliana@223_9: improved Litebase temporary table allocation on Windows 32, Windows CE, Palm, iPhone, and Android. - locked = true; - LOCKVAR(parser); - - // Gets the query result table size and stores it. - resultSetBag = getResultSetBag(resultSet); - plainDB = &resultSetBag->table->db; - if (!muPut(&memoryUsage, selectStmt->selectClause->sqlHashCode, plainDB->db.size, plainDB->dbo.size)) - goto nomem; - UNLOCKVAR(parser); - locked = false; - - return resultSet; -} - -/** - * Drops a table. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param parser The parser. - * @throws DriverException If the table does not exist, if its name is greater than the maximum possible or it is not possible to remove it. - */ -void litebaseExecuteDropTable(Context context, TCObject driver, LitebaseParser* parser) -{ - TRACE("litebaseExecuteDropTable") - Table* table; - CharP tableName = parser->tableList[0]->tableName; - int32 i, - hashCode; - Hashtable* htTables = getLitebaseHtTables(driver); - Heap heap = parser->heap; - TCHARP sourcePath = getLitebaseSourcePath(driver); - - if (xstrlen(tableName) > 23) // The table name has a maximum length because of palm os. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - goto finish; - } - - if ((table = (Table*)TC_htGetPtr(htTables, hashCode = TC_hashCode(tableName)))) // The table is open. - { - Index** columnIndexes = table->columnIndexes; - ComposedIndex** composedIndexes = table->composedIndexes; - Index* index; - - i = table->columnCount; - TC_htRemove(htTables, hashCode); - - while (--i >= 0) // Drops its simple indices. - { - if (columnIndexes[i] && !indexRemove(context, columnIndexes[i])) - goto finish; - columnIndexes[i] = null; - } - - // juliana@223_14: solved possible memory problems. - if ((i = table->numberComposedIndexes - 1) >= 0) - while (--i >= 0) // Drops its composed indices. - { - if ((index = composedIndexes[i]->index) && !indexRemove(context, index)) - goto finish; - composedIndexes[i]->index = null; - } - - if (!freeTable(context, table, true, false)) // Drops the table. - goto finish; - } - else // The table is closed. - { - int32 deleted = 0, - count = 0; - char fileName[DBNAME_SIZE], - fileSimpIdxName[DBNAME_SIZE], - fileCompIdxName[DBNAME_SIZE]; - TCHARPs* list = null; - TCHAR buffer[MAX_PATHNAME]; - char value[DBNAME_SIZE]; - - getDiskTableName(context, OBJ_LitebaseAppCrid(driver), tableName, fileName); - - // juliana@220_12: drop table was dropping a closed table and all tables starting with the same name of the dropped one. - xstrcpy(fileSimpIdxName, fileName); - xstrcpy(fileCompIdxName, fileName); - xstrcat(fileName, "."); - xstrcat(fileSimpIdxName, "$"); - xstrcat(fileCompIdxName, "&"); - - if ((i = TC_listFiles(sourcePath, -1, &list, &count, heap, 0))) // Lists all the path files. - { - fileError(context, i, ""); - goto finish; - } - - while (--count >= 0) // Erases the table files. - { - TC_TCHARP2CharPBuf(list->value, value); - - if (xstrstr(value, fileName) == value || xstrstr(value, fileSimpIdxName) == value || xstrstr(value, fileCompIdxName) == value) - { - getFullFileName(value, sourcePath, buffer); - if ((i = lbfileDelete(null, buffer, false))) - { - fileError(context, i, value); - goto finish; - } - deleted = true; - } - list = list->next; - } - - if (!deleted) // If there is no file to be erased, an exception must be raised. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_NAME_NOT_FOUND), tableName); - goto finish; - } - } - -finish: - heapDestroy(heap); -} - -/** - * Drops an index. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param parser The parser. - * @return -1 if an error occured; the number of indices removed, otherwise. - * @throws DriverException If a column does not have an index, is invalid, or if the columns to have the index dropped are from a primary key. - */ -int32 litebaseExecuteDropIndex(Context context, TCObject driver, LitebaseParser* parser) -{ - TRACE("litebaseExecuteDropIndex") - int32 count = -1, - i; - Table* table = getTable(context, driver, parser->tableList[0]->tableName); - CharP* fieldNames = parser->fieldNames; - - if (!table) - goto finish; - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - if (!setModified(context, table)) - goto finish; - - if (fieldNames[0][0] == '*' && !fieldNames[0][1]) // Drops all the indices. - count = deleteAllIndexes(context, table); - else // Drops an especific index. - { - int32 column = TC_htGet32Inv(&table->htName2index, TC_hashCode(fieldNames[0])), - fieldNamesSize = parser->fieldNamesSize; - - if (fieldNamesSize == 1) // Simple index. - { - if (column < 0) // Unknown column. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_COLUMN_NAME), fieldNames[0]); - goto finish; - } - if (column == table->primaryKeyCol) // Can't use drop index to drop a primary key. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_DROP_PRIMARY_KEY)); - goto finish; - } - - driverDropIndex(context, table, column); - count = 1; - } - else - { - uint8 columns[MAXIMUMS + 1]; - - i = fieldNamesSize; - while (--i >= 0) - { - if ((column = TC_htGet32Inv(&table->htName2index, TC_hashCode(fieldNames[i]))) < 0) // Unknown column. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_COLUMN_NAME), fieldNames[i]); - goto finish; - } - columns[i] = column; - } - if ((i = table->numberComposedPKCols)) - { - uint8* keyCols = table->composedPrimaryKeyCols; - while (--i >= 0) - if (columns[i] != keyCols[i]) - break; - } - if (i < 0) // Can't use drop index to drop a primary key. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_DROP_PRIMARY_KEY)); - goto finish; - } - - driverDropComposedIndex(context, table, columns, fieldNamesSize, -1, true); - count = 1; - } - } - -finish: - heapDestroy(parser->heap); - return count; -} - -/** - * Executes an alter statement. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param parser The parser. - * @throws DriverException If there is no primary key to be dropped, or an invalid column name. - * @throws AlreadyCreatedException If one tries to add another primary key, or a simple primary key is added to a column that already has - * an index. - * @throws SQLParseException If there is a blob in a primary key definition or there is a duplicated column name in the primary key definition. - * @throws OutOfMemoryError If a memory allocation fails. - */ -void litebaseExecuteAlter(Context context, TCObject driver, LitebaseParser* parser) -{ - TRACE("litebaseExecuteAlter") - Table* table = getTable(context, driver, parser->tableList[0]->tableName); - Heap heap, - parserHeap = parser->heap; - CharP* fieldNames = parser->fieldNames; - int32 i; - - if (!table) - goto finish; - heap = table->heap; - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - if (!setModified(context, table)) - goto finish; - - switch (parser->command) - { - case CMD_ALTER_DROP_PK: // DROP PRIMARY KEY - if (table->primaryKeyCol != NO_PRIMARY_KEY) // Simple primary key. - { - i = table->primaryKeyCol; - table->primaryKeyCol = NO_PRIMARY_KEY; - driverDropIndex(context, table, i); - } - else if (table->numberComposedPKCols > 0) // Composed primary key. - { - // juliana@230_17: solved a possible crash or exception if the table is not closed properly after dropping a composed primary key. - // The meta data is saved. - int32 number = table->numberComposedPKCols; - table->numberComposedPKCols = 0; - table->composedPK = NO_PRIMARY_KEY; - driverDropComposedIndex(context, table, table->composedPrimaryKeyCols, number, -1, true); - } - else // There's no primary key. - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY)); - break; - - case CMD_ALTER_ADD_PK: // ADD PRIMARY KEY - { - int32 size = parser->fieldNamesSize, - j, - colIndex = -1; - int32* hashCols = (int32*)TC_heapAlloc(heap, size << 2); - int8* types = table->columnTypes; - uint8* composedPKCols = (uint8*)TC_heapAlloc(heap, size); - Hashtable* htName2index = &table->htName2index; - - IF_HEAP_ERROR(heap) // juliana@223_14: solved possible memory problems. - { - table->primaryKeyCol = table->composedPK = NO_PRIMARY_KEY; - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - break; - } - - if (table->primaryKeyCol != NO_PRIMARY_KEY || table->composedPK != NO_PRIMARY_KEY) // There can't be two primary keys. - { - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_PRIMARY_KEY_ALREADY_DEFINED)); - break; - } - - i = -1; - while (++i < size) - { - if ((colIndex = TC_htGet32Inv(htName2index, hashCols[i] = TC_hashCode(fieldNames[i]))) < 0) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_COLUMN_NAME), fieldNames[i]); - goto finish; - } - - // Verifies if there's a duplicate definition. - j = i; - while (--j >= 0) - if (composedPKCols[j] == colIndex) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DUPLICATED_COLUMN_NAME), fieldNames[i]); - goto finish; - } - - composedPKCols[i] = colIndex; - if (types[colIndex] == BLOB_TYPE) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_BLOB_INDEX)); - goto finish; - } - } - - if (size == 1) // Simple primary key. - { - // juliana@230_41: an AlreadyCreatedException is now thrown when trying to add a primary key for a column that already has a simple index. - if (table->columnIndexes[colIndex]) - { - IntBuf intBuf; - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_INDEX_ALREADY_CREATED), TC_int2str(colIndex, intBuf)); - break; - } - else // If there is no index yet for the column, creates it. - { - table->primaryKeyCol = colIndex; - if (!driverCreateIndex(context, table, hashCols, 1, 1, null)) - { - table->primaryKeyCol = -1; - break; - } - } - - } - else //composed primary key - { - // juliana@202_20: Corrected a bug that would not create the composed PK correctly with alter table. - table->numberComposedPKCols = size; - table->composedPrimaryKeyCols = composedPKCols; - table->composedPK = table->numberComposedIndexes; - if (!driverCreateIndex(context, table, hashCols, 1,size, composedPKCols)) - { - table->composedPK = -1; - break; - } - - } - break; - } - - case CMD_ALTER_RENAME_TABLE: // RENAME TABLE - if (tableExistsByName(context, driver, fieldNames[0])) // The new table name is stored in the field list. - break; - if (!renameTable(context, driver, table, fieldNames[0])) // juliana@223_14: solved possible memory problems. - renameTable(context, driver, table, parser->tableList[0]->tableName); - break; - - case CMD_ALTER_RENAME_COLUMN: // RENAME COLUMN - { - CharP oldColumn = fieldNames[1], - newColumn; - uint32 length = xstrlen(fieldNames[0]); - bool reuseSpace = (length <= xstrlen(oldColumn)); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - break; - } - - if (reuseSpace) // If the new column name length is smaller than the previous one, reuses its space. - newColumn = fieldNames[0]; - else - { - newColumn = (CharP)TC_heapAlloc(heap, length + 1); - xmemmove(newColumn, fieldNames[0], length); - } - renameTableColumn(context, table, oldColumn, newColumn, reuseSpace); - break; - } - - case CMD_ALTER_ADD_COLUMN: - { - SQLFieldDefinition* field = parser->fieldList[0]; - int32 oldCount = table->columnCount, - newCount = oldCount + 1, - bytes = (oldCount + 7) >> 3, - hash = TC_hashCode(field->fieldName); - - if (TC_htGet32Inv(&table->htName2index, hash) >= 0) - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DUPLICATED_COLUMN_NAME), field->fieldName); - else if (oldCount == MAXIMUMS) - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_MAX_NUM_FIELDS_REACHED)); - else - { - uint8* newAttrs = TC_heapAlloc(heap, newCount); - uint8* newTypes = TC_heapAlloc(heap, newCount); - uint8* newBuffer; - uint8* columnNulls; - uint16* newOffsets = TC_heapAlloc(heap, (newCount + 1) << 1); - int32* newHashes = (int32*)TC_heapAlloc(heap, newCount << 2); - int32* newSizes = (int32*)TC_heapAlloc(heap, newCount << 2); - CharP* newNames = (CharP*)TC_heapAlloc(heap, newCount * TSIZE); - SQLValue** newDefaultValues = (SQLValue**)TC_heapAlloc(heap, newCount * TSIZE); - SQLValue** record = (SQLValue**)TC_heapAlloc(parserHeap, newCount * TSIZE); - SQLValue* newDefaultValue = null; - Index** newIndices = (Index**)TC_heapAlloc(heap, newCount * TSIZE); - JCharP defaultValue = field->defaultValue; - int32 type = field->fieldType; - PlainDB newDB, - oldDB; - PlainDB* plainDB = &table->db; - char tempName[DBNAME_SIZE + 1]; - bool isNull = (field->defaultValue == null), - useCrypto = plainDB->db.useCrypto; - int32 crc32, - j, - k, - size, - length, - blobLength; - - if (((oldCount + 8) >> 3) > bytes) // Increases the nulls fields if the number of bytes must be increased. - { - columnNulls = table->columnNulls = TC_heapAlloc(heap, ++bytes); - table->storeNulls = TC_heapAlloc(heap, bytes); - } - else - columnNulls = table->columnNulls; - - // Increases all the columns. - - // Column attrs. - xmemmove(newAttrs, table->columnAttrs, oldCount); - (table->columnAttrs = newAttrs)[oldCount] = (field->defaultValue? ATTR_COLUMN_HAS_DEFAULT : 0) - | (field->isNotNull? ATTR_COLUMN_IS_NOT_NULL : 0); - - // Column hashes. - xmemmove(newHashes, table->columnHashes, oldCount << 2); - TC_htPut32(&table->htName2index, (table->columnHashes = newHashes)[oldCount] = hash, oldCount); - - // Column offsets. - xmemmove(newOffsets, table->columnOffsets, newCount << 1); - (table->columnOffsets = newOffsets)[newCount] = (newOffsets[oldCount] + typeSizes[field->fieldType]); - length = table->columnOffsets[newCount] + NUMBEROFBYTES(newCount); - - // Column types. - xmemmove(newTypes, table->columnTypes, oldCount); - (table->columnTypes = (int8*)newTypes)[oldCount] = type; - - // Column sizes. - xmemmove(newSizes, table->columnSizes, oldCount << 2); - (table->columnSizes = newSizes)[oldCount] = field->fieldSize; - - // Column names. - xmemmove(newNames, table->columnNames, oldCount * TSIZE); - (table->columnNames = newNames)[oldCount] = TC_heapAlloc(heap, i = (xstrlen(field->fieldName) + 1)); - xmemmove(newNames[oldCount], field->fieldName, i); - - // Default values. - xmemmove(newDefaultValues, table->defaultValues, oldCount * TSIZE); - table->defaultValues = newDefaultValues; - if (defaultValue) // Sets the new default value if it exists. - { - bool error = false; - DoubleBuf doubleBuf; - - newDefaultValue = newDefaultValues[oldCount] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - i = TC_JCharPLen(defaultValue); - if ((type != CHARS_TYPE && type != CHARS_NOCASE_TYPE && i > 39) - || ((type == CHARS_TYPE || type == CHARS_NOCASE_TYPE) && i > field->fieldSize)) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER)); - goto finish; - } - - // juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than - // the type range. - switch (field->fieldType) - { - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - newDefaultValue->asChars = (JCharP)TC_heapAlloc(heap, (i + 1) << 1); - xmemmove(newDefaultValue->asChars, defaultValue, (newDefaultValue->length = i) << 1); - break; - case SHORT_TYPE: - newDefaultValue->asShort = str2short(TC_JCharP2CharPBuf(defaultValue, i, doubleBuf), &error); - break; - case INT_TYPE: - newDefaultValue->asInt = TC_str2int(TC_JCharP2CharPBuf(defaultValue, i, doubleBuf), &error); - break; - case LONG_TYPE: - newDefaultValue->asLong = TC_str2long(TC_JCharP2CharPBuf(defaultValue, i, doubleBuf), &error); - break; - case FLOAT_TYPE: - newDefaultValue->asFloat = str2float(TC_JCharP2CharPBuf(defaultValue, i, doubleBuf), &error); - break; - case DOUBLE_TYPE: - newDefaultValue->asDouble = TC_str2double(TC_JCharP2CharPBuf(defaultValue, i, doubleBuf), &error); - break; - case DATE_TYPE: - case DATETIME_TYPE: - TC_JCharP2CharPBuf(defaultValue, i, doubleBuf); - if (!testAndPrepareDateAndTime(context, newDefaultValue, doubleBuf, type)) - goto finish; - } - } - - // Column indices. - xmemmove(newIndices, table->columnIndexes, oldCount * TSIZE); - table->columnIndexes = newIndices; - - // Sets the new plain db. - xstrcpy(tempName, table->name); - xstrcat(tempName, "_"); - xmemzero(&newDB, sizeof(PlainDB)); - xmemmove(&oldDB, plainDB, sizeof(PlainDB)); - if (!(createPlainDB(context, &newDB, tempName, true, useCrypto, table->sourcePath))) - goto finish; - newDB.isAscii = oldDB.isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - newDB.headerSize = oldDB.headerSize; - newBuffer = newDB.basbuf = TC_heapAlloc(heap, i = newOffsets[newCount] + ((newCount + 7) >> 3) + 4); - plainSetRowSize(&newDB, i, newBuffer); - newDB.rowInc = oldDB.rowCount; - - record[oldCount] = newDefaultValue; // Sets the record for the new column. - -#if defined(POSIX) || defined(ANDROID) - removeFileFromList(&newDB.db); - removeFileFromList(&newDB.dbo); -#endif - - // Saves the new meta data. - xmemmove(plainDB, &newDB, sizeof(PlainDB)); - table->columnCount++; - tableSaveMetaData(context, table, TSMD_EVERYTHING); - - xmemmove(&newDB, plainDB, sizeof(PlainDB)); - xmemmove(plainDB, &oldDB, sizeof(PlainDB)); - table->columnCount--; - - size = oldDB.rowCount; - - i = oldCount; - while (--i >= 0) - { - record[i] = (SQLValue*)TC_heapAlloc(parserHeap, sizeof(SQLValue)); - if (newTypes[i] == CHARS_TYPE || newTypes[i] == CHARS_NOCASE_TYPE) - record[i]->asChars = (JCharP)TC_heapAlloc(parserHeap, (newSizes[i] << 1) + 2); - else if (newTypes[i] == BLOB_TYPE) - record[i]->asBlob = (uint8*)TC_heapAlloc(parserHeap, newSizes[i]); - } - - i = -1; - while (++i < size) - { - if (!readRecord(context, table, record, i, columnNulls, null, 0, false, null, null)) // juliana@220_3 juliana@227_20 - { -free: - plainRemove(context, &newDB, table->sourcePath); - goto finish; - } - j = -1; - - if (isNull) - columnNulls[oldCount >> 3] |= (1 << (oldCount & 7)); // Sets the column as null. - - while (++j < newCount) - if (!writeValue(context, &newDB, record[j], &newBuffer[newOffsets[j]], newTypes[j], newSizes[j], true, true, (columnNulls[j >> 3] & (1 << (j & 7))), false)) - goto free; - xmemmove(&newBuffer[newOffsets[j]], columnNulls, bytes); - - // juliana@230_12: improved recover table to take .dbo data into consideration. - // juliana@223_8: corrected a bug on purge that would not copy the crc32 codes for the rows. - // juliana@220_4: added a crc32 code for every record. Please update your tables. - k = newBuffer[3]; - newBuffer[3] = 0; // juliana@222_5: The crc was not being calculated correctly for updates. - newDB.useOldCrypto = false; - - // Computes the crc for the record and stores at the end of the record. - crc32 = updateCRC32(newBuffer, length, 0); - if (table->version == VERSION_TABLE) - { - j = newCount; - while (--j > 0) - if ((newTypes[j] == CHARS_TYPE || newTypes[j] == CHARS_NOCASE_TYPE) - && (columnNulls[j >> 3] & (1 << (j & 7))) == 0) - { - crc32 = updateCRC32((uint8*)record[j]->asChars, record[j]->length << 1, crc32); - } - else if (newTypes[j] == BLOB_TYPE && (columnNulls[j >> 3] & (1 << (j & 7))) == 0) - { - blobLength = record[j]->length; - crc32 = updateCRC32((uint8*)&blobLength, 4, crc32); - } - } - xmove4(&newBuffer[length], &crc32); // Computes the crc for the record and stores at the end of the record. - newBuffer[3] = k; - - if (!plainAdd(context, &newDB) || !plainWrite(context, &newDB)) - goto free; - } - - // Puts the new plain db in the table and deletes the old one. - newDB.rowInc = DEFAULT_ROW_INC; - - if (!plainRemove(context, &oldDB, table->sourcePath) || !plainRename(context, &newDB, table->name, table->sourcePath)) - goto finish; - xstrcpy(newDB.name, table->name); - xmemmove(&table->db, &newDB, sizeof(PlainDB)); - table->columnCount++; - - i = newCount; - while (--i >= 0) // Recreates the simple indices. - if (newIndices[i] && (newTypes[i] == CHARS_TYPE || newTypes[i] == CHARS_NOCASE_TYPE) && !tableReIndex(context, table, i, false, null)) - goto finish; - - if ((i = table->numberComposedIndexes) > 0) // Recreates the composed indices. - while (--i >= 0) - if (!tableReIndex(context, table, -1, false, table->composedIndexes[i])) - goto finish; - } - } - } - -finish: - heapDestroy(parserHeap); -} - -// -// This is how the attributes flow: -// -// Insert: -// new -// update: -// if (synced or updated) then updated -// if (new) then new -// delete: -// deleted -////////////////////////////////////////////////////////////////////////// -// juliana@225_14: RowIterator must throw an exception if its driver is closed. -/** - * Gets the value of a column stored in the row iterator. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The column index. - * @param type The type of the column. - * @param p->retI Receives an int or a short. - * @param p->retL Receives a long. - * @param p->retD Receives a float or a double. - * @param p->retO Receives a string, blob, date, or datetime. - * @throws DriverException If the column is not of type requested. - * @throws IllegalArgumentException If the column index is invalid. - */ -void getByIndex(NMParams p, int32 type) -{ - TRACE("getByIndex") - - if (testRIClosed(p)) // The row iterator and its driver can't be closed. - { - TCObject rowIterator = p->obj[0]; - Context context = p->currentContext; - Table* table = getRowIteratorTable(rowIterator); - uint8* data = (uint8*)ARRAYOBJ_START(OBJ_RowIteratorData(rowIterator)); - int32 column = p->i32[0], - i; - - if (column < 0 || column >= table->columnCount) // Checks if the column index is within range. - { - TC_throwExceptionNamed(context, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER), column); - return; - } - - if (isBitSet(table->columnNulls, column)) // juliana@223_5: now possible null values are treated in RowIterator. - { - p->retD = 0; - p->retO = null; - return; - } - - // If the column type is char nocase, the column is compatible with the char type. - if ((i = table->columnTypes[column]) == CHARS_NOCASE_TYPE) - i = CHARS_TYPE; - - if (i != type) // The column type and the desired type must be compatible. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return; - } - - switch (type) - { - case SHORT_TYPE: // juliana@227_18: corrected a possible insertion of a negative short column being recovered in the select as positive. - p->retI = 0; - xmove2(&p->retI, &data[table->columnOffsets[column]]); - break; - case INT_TYPE: - xmove4(&p->retI, &data[table->columnOffsets[column]]); - break; - case LONG_TYPE: - xmove8(&p->retL, &data[table->columnOffsets[column]]); - break; - case FLOAT_TYPE: - { - float floatNum; - xmove4(&floatNum, &data[table->columnOffsets[column]]); - p->retD = floatNum; - break; - } - case DOUBLE_TYPE: - READ_DOUBLE((uint8*)&p->retD, &data[table->columnOffsets[column]]); - break; - case BLOB_TYPE: - { - int32 size; - XFile* file = &table->db.dbo; - - xmove4(&i, &data[table->columnOffsets[column]]); - nfSetPos(file, i); // Finds the blob position in the .dbo. - - // Finds the blob size and creates the returning blob object. - if (!nfReadBytes(context, file, (uint8*)&size, 4) - || (!(p->retO = TC_createArrayObject(context, BYTE_ARRAY, size)))) - return; - - TC_setObjectLock(p->retO, UNLOCKED); - - if (!nfReadBytes(context, file, ARRAYOBJ_START(p->retO), size)) // Reads the blob. - return; - - break; - } - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - { - int32 size = 0; - XFile* file = &table->db.dbo; - - xmove4(&i, &data[table->columnOffsets[column]]); // Finds the string position in the .dbo. - nfSetPos(file, i); // Finds the string position in the .dbo. - - // Finds the string size and creates the returning string object. - if (!nfReadBytes(context, file, (uint8*)&size, 2) - || (!(p->retO = TC_createStringObjectWithLen(context, size)))) - return; - - TC_setObjectLock(p->retO, UNLOCKED); - loadString(context, &table->db, String_charsStart(p->retO), size); - break; - } - case DATE_TYPE: - { - int32 date; - xmove4(&date, &data[table->columnOffsets[column]]); // Reads the date. - setDateObject(p, date); - break; - } - case DATETIME_TYPE: - { - uint8* buffer = &data[table->columnOffsets[column]]; - int32 date, - time; - xmove4(&date, buffer); - xmove4(&time, buffer + 4); - setTimeObject(p, date, time); - } - } - } -} - -/** - * Tests if the row iterator or the driver where it was created is closed. - * - * @param p->obj[0] The row iterator object. - * @throws IllegalStateException If the row iterator or driver is closed. - */ -bool testRIClosed(NMParams params) -{ - TRACE("testRIClosed") - TCObject rowIterator = params->obj[0]; - - if (OBJ_LitebaseDontFinalize(OBJ_RowIteratorDriver(rowIterator))) // The connection with Litebase can't be closed. - { - TC_throwExceptionNamed(params->currentContext, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - return false; - } - if (!(getRowIteratorTable(rowIterator))) // Row iterator closed. - { - TC_throwExceptionNamed(params->currentContext, "java.lang.IllegalStateException", getMessage(ERR_ROWITERATOR_CLOSED)); - return false; - } - return true; -} - -// juliana@226_2: corrected possible path problems on Windows CE and Palm. -/** - * Checks if the path passed as a parameter is valid and uses an internal path if it is null. - * - * @param context The thread context where the function is being executed. - * @param sourcePath Receives the path that Litebase will use to store and access tables. - * @param pathParam the path passed as a parameter. - * @return false if an error occurs; true, otherwise. - * @throws DriverException if the path passed as a parameter is invalid. - */ -bool checkApppath(Context context, TCHARP sourcePath, TCHARP pathParam) // juliana@214_1 -{ - TRACE("checkApppath") - TCHAR buffer[MAX_PATHNAME]; - int32 endCh, - lenAppPath, - ret = 0; - - if (pathParam) - { - // juliana@252_3: corrected a possible crash if the path had more than 255 characteres. - if (tcslen(pathParam) + 1 > MAX_PATHNAME) - { - char pathCharP[1024]; - TC_TCHARP2CharPBuf(pathParam, pathCharP); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), pathCharP); - return false; - } - //tcscpy(sourcePath, tstrTrim(pathParam)); - guich: was crashing on LaudoMovel - {TCHARP src = tstrTrim(pathParam); xmemmove(sourcePath, src, sizeof(TCHAR) * (tcslen(src)+1));} - - if (!sourcePath[0]) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), ""); - return false; - } - } - else // Since no path was passed by the user, gets it from dataPath or appPath. - getCurrentPath(sourcePath); - - tcscpy(buffer, tstrTrim(sourcePath)); - - tcscpy(sourcePath, buffer); - -// juliana@214_1: relative paths can't be used with Litebase. - if (!TC_validatePath(sourcePath)) - { - char pathCharP[1024]; - TC_TCHARP2CharPBuf(sourcePath, pathCharP); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), pathCharP); - return false; - } - - // Creates the path folder if it does not exist. - if (!sourcePath[0] || (sourcePath[0] && !lbfileExists(sourcePath) && (ret = lbfileCreateDir(sourcePath)))) - { - char pathCharP[1024]; - - TC_TCHARP2CharPBuf(sourcePath, pathCharP); - if (!ret) - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), pathCharP); - else - fileError(context, ret, pathCharP); - return false; - } - - // Puts a '/' at the end of the path. - lenAppPath = tcslen(sourcePath); - endCh = lenAppPath > 0? sourcePath[lenAppPath - 1] : -1; - if ((endCh != '\\' && endCh != '/') || endCh == -1) - tcscat(sourcePath, TEXT("/")); - - return true; -} - -/** - * Verifies if the function can be applied to a data type field. - * - * @param parameterDataType The data type of the function parameter. - * @param sqlFunction The function code. - * @return true If the function can be applied to a data type field; false, otherwise. - */ -bool bindFunctionDataType(int32 parameterDataType, int32 sqlFunction) // rnovais@568_10 -{ - TRACE("bindFunctionDataType") - - int32 length = 7; // Maximum number of functions for a data type. - int8* functions; - - if (parameterDataType < 0 || parameterDataType > DATETIME_TYPE || sqlFunction <= FUNCTION_DT_NONE || sqlFunction > FUNCTION_DT_LOWER) - return false; - - functions = function_x_datatype[parameterDataType]; - - while (--length >= 0) - if (*functions++ == sqlFunction) - return true; - return false; -} - -/** - * Returns the name of the data type function. - * - * @param sqlFunction The function code. - * @return A string with the function name. - */ -CharP dataTypeFunctionsName(int32 sqlFunction) // rnovais@568_10 -{ - TRACE("dataTypeFunctionsName") - if (sqlFunction <= FUNCTION_DT_LOWER && sqlFunction >= FUNCTION_DT_YEAR) - return names[sqlFunction]; - return ""; -} - -/** - * Checks if the driver is opened and another parameter is not null when they are sent as parameters in some native methods. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The parameter to be checked. - * @param parameter The name of the parameter that can't be null. - * @throws IllegalStateException If the driver is closed. - * @throws NullPointerException If the table name is null. - */ -bool checkParamAndDriver(NMParams params, CharP parameter) -{ - if (OBJ_LitebaseDontFinalize(params->obj[0])) // The driver can't be closed. - { - TC_throwExceptionNamed(params->currentContext, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - return false; - } - if (!params->obj[1]) // The parameter can't be null. - { - TC_throwNullArgumentException(params->currentContext, parameter); - return false; - } - return true; -} - -/** - * Encrypts or decrypts all the tables of a connection given from the application id. - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - * @throws DriverException If a file error occurs, not all the tables use the desired cryptography format. - * @throws NullPointerException If one of the string parameters is null. - */ -void encDecTables(NMParams params, bool toEncrypt) -{ - TRACE("encDecTables") - TCObject cridObj = params->obj[0], - pathObj = params->obj[1]; - - if (cridObj) - if (pathObj) - if (String_charsLen(pathObj) >= MAX_PATHNAME - 4 - DBNAME_SIZE) // The path length can't be greater than the buffer size. - TC_throwExceptionNamed(params->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_PATH), ""); - else - { - TCHARPs* list = null; - TCHAR pathStr[MAX_PATHNAME]; // juliana@230_6 - char cridStr[5], - value[DBNAME_SIZE]; - int32 i, - j, - k, - error, - count = 0, - encByte = toEncrypt? 0 : 1; - Heap heap = heapCreate(); - TCHAR buffer[MAX_PATHNAME]; - NATIVE_FILE file; - uint8 bytes[CACHE_INITIAL_SIZE]; - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(params->currentContext, "java.lang.OutOfMemoryError", null); - -error: - heapDestroy(heap); - return; - } - - TC_JCharP2CharPBuf(String_charsStart(cridObj), String_charsLen(cridObj), cridStr); - TC_JCharP2TCHARPBuf(String_charsStart(pathObj), String_charsLen(pathObj), pathStr); - - if ((error = TC_listFiles(pathStr, -1, &list, &count, heap, 0))) // Lists all the files of the folder. - { - fileError(params->currentContext, error, ""); - goto error; - } - - // Before doing anything, checks if all the .db files are marked as decrypted or encrypted. - i = count; - while (--i >= 0) - { - TC_TCHARP2CharPBuf(list->value, value); - if (xstrstr(value, cridStr) == value && xstrstr(value, ".db") && !xstrstr(value, ".dbo")) - { - getFullFileName(value, pathStr, buffer); - if ((error = lbfileCreate(&file, buffer, READ_ONLY))) - { -fileError: - fileError(params->currentContext, error, value); - goto error; - } - if ((error = lbfileReadBytes(file, (CharP)bytes, 0, 4, &j)) || (error = lbfileClose(&file))) - goto fileError; - if ((toEncrypt? bytes[0] != 0 : (bytes[0] != 1 && bytes[0] != 3)) || bytes[1] != 0 || bytes[2] != 0 || bytes[3] != 0) - { - TC_throwExceptionNamed(params->currentContext, "litebase.DriverException", getMessage(ERR_WRONG_CRYPTO_FORMAT)); - goto error; - } - } - - list = list->next; - } - - i = count; - encByte = toEncrypt? 1 : 0; - while (--i >= 0) - { - - TC_TCHARP2CharPBuf(list->value, value); - if (xstrstr(value, cridStr) == value) - { - getFullFileName(value, pathStr, buffer); - if ((error = lbfileCreate(&file, buffer, READ_WRITE))) - goto fileError; - - count = 0; - if (xstrstr(value, ".db")) // Changes the .db file crypto information. - { - if ((error = lbfileReadBytes(file, (CharP)bytes, 0, 4, &j))) - goto fileError; - bytes[0] = encByte; - if ((error = lbfileSetPos(file, 0)) || (error = lbfileWriteBytes(file, (CharP)bytes, 0, 4, &j))) - goto fileError; - count = 4; - } - - // Encrypts or decrypts all the table files data. - while (!(error = lbfileReadBytes(file, (CharP)bytes, 0, CACHE_INITIAL_SIZE, &j)) && j) - { - k = j; - while (--k >= 0) - bytes[k] ^= 0xAA; - - if ((error = lbfileSetPos(file, count)) || (error = lbfileWriteBytes(file, (CharP)bytes, 0, j, &j))) - goto fileError; - count += j; - } - - if (error || (error = lbfileClose(&file))) - { - lbfileClose(&file); - goto fileError; - } - - } - - list = list->next; - } - - heapDestroy(heap); - - } - else // The string argument can't be null. - TC_throwNullArgumentException(params->currentContext, "sourcePath"); - else // The string argument can't be null. - TC_throwNullArgumentException(params->currentContext, "crid"); - -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if LibClose() finished some structures. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(LibClose) -{ - UNUSED(currentContext) - LibClose(); - - // All Created drivers are closed. - ASSERT1_EQUALS(Null, htCreatedDrivers.items); - ASSERT1_EQUALS(Null, htCreatedDrivers.heap); - ASSERT2_EQUALS(I32, htCreatedDrivers.size, 0); - -finish : ; -} - -/** - * Tests if LibOpen() initializes all the needed variables. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(LibOpen) -{ - int32 i = '@'; - TOpenParams openParams; - - openParams.currentContext = currentContext; - openParams.getProcAddress = TC_getProcAddress; - ASSERT1_EQUALS(True, LibOpen(&openParams)); - - // The TCVM functions needed by Litebase. - ASSERT1_EQUALS(NotNull, TC_CharP2JCharP); - ASSERT1_EQUALS(NotNull, TC_CharP2JCharPBuf); - ASSERT1_EQUALS(NotNull, TC_CharP2TCHARPBuf); - ASSERT1_EQUALS(NotNull, TC_CharPToLower); - ASSERT1_EQUALS(NotNull, TC_JCharP2CharP); - ASSERT1_EQUALS(NotNull, TC_JCharP2CharPBuf); - ASSERT1_EQUALS(NotNull, TC_JCharP2TCHARPBuf); - ASSERT1_EQUALS(NotNull, TC_JCharPEqualsJCharP); - ASSERT1_EQUALS(NotNull, TC_JCharPEqualsIgnoreCaseJCharP); - ASSERT1_EQUALS(NotNull, TC_JCharPHashCode); - ASSERT1_EQUALS(NotNull, TC_JCharPIndexOfJChar); - ASSERT1_EQUALS(NotNull, TC_JCharPLen); - ASSERT1_EQUALS(NotNull, TC_JCharToLower); - ASSERT1_EQUALS(NotNull, TC_JCharToUpper); - ASSERT1_EQUALS(NotNull, TC_TCHARP2CharPBuf); - ASSERT1_EQUALS(NotNull, TC_alert); - ASSERT1_EQUALS(NotNull, TC_appendCharP); // juliana@230_30 - ASSERT1_EQUALS(NotNull, TC_appendJCharP); // juliana@230_30 - ASSERT1_EQUALS(NotNull, TC_areClassesCompatible); - ASSERT1_EQUALS(NotNull, TC_createArrayObject); - ASSERT1_EQUALS(NotNull, TC_createObject); - ASSERT1_EQUALS(NotNull, TC_createStringObjectFromCharP); - ASSERT1_EQUALS(NotNull, TC_createStringObjectFromTCHARP); - ASSERT1_EQUALS(NotNull, TC_createStringObjectWithLen); - ASSERT1_EQUALS(NotNull, TC_debug); - ASSERT1_EQUALS(NotNull, TC_double2str); - ASSERT1_EQUALS(NotNull, TC_executeMethod); - ASSERT1_EQUALS(NotNull, TC_getApplicationId); - ASSERT1_EQUALS(NotNull, TC_getAppPath); - ASSERT1_EQUALS(NotNull, TC_getDataPath); - ASSERT1_EQUALS(NotNull, TC_getDateTime); - ASSERT1_EQUALS(NotNull, TC_getErrorMessage); - ASSERT1_EQUALS(NotNull, TC_getSettingsPtr); - ASSERT1_EQUALS(NotNull, TC_getTimeStamp); - ASSERT1_EQUALS(NotNull, TC_hashCode); - ASSERT1_EQUALS(NotNull, TC_hashCodeFmt); - ASSERT1_EQUALS(NotNull, TC_heapAlloc); - ASSERT1_EQUALS(NotNull, TC_heapDestroyPrivate); - ASSERT1_EQUALS(NotNull, TC_hstrdup); - ASSERT1_EQUALS(NotNull, TC_htFree); - ASSERT1_EQUALS(NotNull, TC_htFreeContext); - ASSERT1_EQUALS(NotNull, TC_htGet32); - ASSERT1_EQUALS(NotNull, TC_htGet32Inv); - ASSERT1_EQUALS(NotNull, TC_htGetPtr); - ASSERT1_EQUALS(NotNull, TC_htNew); - ASSERT1_EQUALS(NotNull, TC_htPut32); - ASSERT1_EQUALS(NotNull, TC_htPut32IfNew); - ASSERT1_EQUALS(NotNull, TC_htPutPtr); - ASSERT1_EQUALS(NotNull, TC_htRemove); - ASSERT1_EQUALS(NotNull, TC_int2CRID); - ASSERT1_EQUALS(NotNull, TC_int2str); - ASSERT1_EQUALS(NotNull, TC_listFiles); - ASSERT1_EQUALS(NotNull, TC_loadClass); - ASSERT1_EQUALS(NotNull, TC_long2str); - ASSERT1_EQUALS(NotNull, TC_privateHeapCreate); - ASSERT1_EQUALS(NotNull, TC_privateHeapSetJump); - ASSERT1_EQUALS(NotNull, TC_privateXfree); - ASSERT1_EQUALS(NotNull, TC_privateXmalloc); - ASSERT1_EQUALS(NotNull, TC_privateXrealloc); - ASSERT1_EQUALS(NotNull, TC_setObjectLock); - ASSERT1_EQUALS(NotNull, TC_str2double); - ASSERT1_EQUALS(NotNull, TC_str2int); - ASSERT1_EQUALS(NotNull, TC_str2long); - ASSERT1_EQUALS(NotNull, TC_throwExceptionNamed); - ASSERT1_EQUALS(NotNull, TC_throwNullArgumentException); - ASSERT1_EQUALS(NotNull, TC_tiF_create_sii); - ASSERT1_EQUALS(NotNull, TC_toLower); - ASSERT1_EQUALS(NotNull, TC_trace); - ASSERT1_EQUALS(NotNull, TC_validatePath); // juliana@214_1 - -#ifdef ENABLE_MEMORY_TEST - ASSERT1_EQUALS(NotNull, TC_getCountToReturnNull); - ASSERT1_EQUALS(NotNull, TC_setCountToReturnNull); -#endif - - // A hash table for the loaded connections. - ASSERT1_EQUALS(NotNull, htCreatedDrivers.items); - ASSERT1_EQUALS(Null, htCreatedDrivers.heap); - ASSERT2_EQUALS(I32, htCreatedDrivers.size, 0); - ASSERT2_EQUALS(I32, htCreatedDrivers.hash, 9); - ASSERT2_EQUALS(I32, htCreatedDrivers.threshold, 10); - - // A hash table for select statistics. - ASSERT1_EQUALS(NotNull, memoryUsage.items); - ASSERT2_EQUALS(I32, memoryUsage.size, 0); - ASSERT2_EQUALS(I32, memoryUsage.hash, 99); - ASSERT2_EQUALS(I32, memoryUsage.threshold, 100); - - // Error messages. - // English messages. - // General errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[0], "Error: ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[1], " Near position %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[2], "Syntax error.")); - - // Limit errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[3], "Maximum number of different fields was reached.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[4], "Maximum number of parameters in the 'WHERE/HAVING' clause was reached.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[5], "Maximum number of composed indices 32 was reached.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[6], "Table name too big: must be <= 23.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[7], "The maximum number of fields in a SELECT clause was exceeded.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[8], "Maximum number of columns exceeded in the 'ORDER BY/GROUP BY' clause.")); - - // Column errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[9], "Unknown column %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[10], "Invalid column name: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[11], "Invalid column number: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[12], "The following column(s) does (do) not have an associated index %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[13], "Column name in field list is ambiguous: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[14], "Column not found %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[15], "Duplicated column name: %s.")); - - // Primary key errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[16], "A primary key was already defined for this table.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[17], "Table does not have a primary key.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[18], "Statement creates a duplicated primary key in %s.")); - - // Type errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[19], "Incompatible types.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[20], "Field size must be a positive interger value.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[21], "Value %s is not a valid number for the desired type: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[22], "Incompatible data type for the function call: %s")); - - // Number of fields errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[23], "The number of fields does not match the number of values ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[24], "The given number of values does not match the table definition %d.")); - - // Default value errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[25], "Length of default value is bigger than column size.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[26], "An added column declared as NOT NULL must have a not null default value.")); - - // Driver errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[27], "This driver instance was closed and cannot be used anymore. Please get a new instance of it.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[28], "ResultSet already closed!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[29], "ResultSetMetaData cannot be used after the ResultSet is closed.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[30], "The application id must be four characters long.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[31], "The increment must be greater than 0 or -1.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[32], "Iterator already closed.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[33], "Prepared statement closed. Please prepare it again.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[34], "Invalid connection parameter: %s.")); - - // Table errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[35], "Table name not found: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[36], "Table already created: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[37], "It is not possible to open a table within a connection with a different string format.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[38], "It is not possible to open a table within a connection with a different cryptography format.")); - - // ROWID errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[39], "ROWID can't be changed by the user!")); - - // Prepared Statement errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[40], "Query does not return result set.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[41], "Query does not perform updates in the database.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[42], "Not all parameters of the query had their values defined.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[43], "A value was not defined for the parameter %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[44], "Invalid parameter index.")); - - // Rename errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[45], "Can't rename table. This table already exists: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[46], "Column already exists: %s.")); - - // Alias errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[47], "Not unique table/alias: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[48], "This alias is already being used in this expression: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[49], "An alias is required for the aggregate function column.")); - - // Litebase.execute() error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[50], "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute().")); - - // Order by and group by errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[51], "ORDER BY and GROUP BY clauses must match.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[52], "No support for virtual columns in SQL queries with GROUP BY clause.")); - - // Function errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[53], "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[54], - "%s is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation functions.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[55], - "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[56], "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[57], -"%s was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation funtions." -)); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[58], "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields.")); - - // DATE and DATETIME errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[59], "Value is not a DATE: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[60], "Value is not a DATETIME: %s.")); - - // Index error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[61], "Index already created for column %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[62], "Can't drop a primary key index withdrop index.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[63], "Index too large. It can't have more than 65534 nodes.")); - - // NOT NULL errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[64], "Primary key can't have null.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[65], "Field can't be null: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[66], "A parameter in a where clause can't be null.")); - - // Result set errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[67], "ResultSet in invalid record position: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[68], "Invalid value for decimal places: %d. It must range from -1 to 40.")); - - // File errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[69], "Can't read from table %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[70], "Can't load leaf node!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[71], "Table is corrupted: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[72], "Table not closed properly: %s.")); // juliana@220_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[73], "A properly closed table can't be used in recoverTable(): %s.")); // juliana@222_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[74], "Can't find index record position on delete.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[75], "The table format (%d) is incompatible with Litebase version. Please update your tables.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[76], "The table format is not the previous one: %s.")); // juliana@220_11 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[77], "Invalid path: %s.")); // juliana@214_1 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[78], "Invalid file position: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[79], "Database not found.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[80], "An opened table can't be recovered or converted: %s.")); - - // BLOB errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[81], "The total size of a blob can't be greater then 10 Mb.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[82], "This is not a valid size multiplier.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[83], "A blob type can't be part of a primary key.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[84], "A BLOB column can't be indexed.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[85], "A BLOB can't be in the where clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[86], "A BLOB can't be converted to a string.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[87], "Blobs types can't be in ORDER BY or GROUP BY clauses.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[88], "It is not possible to compare BLOBs.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[89], "It is only possible to insert or update a BLOB through prepared statements using setBlob().")); - - // Portuguese messages. - // General errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[0], "Erro: ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[1], " Pr�ximo � posi��o %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[2], "Erro de sintaxe.")); - - // Limit errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[3], "N�mero m�ximo de campos diferentes foi alcan�ado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[4], "N�mero m�ximo da lista de par�metros na cl�usula 'WHERE/HAVING' foi alcan�ado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[5], "Numero m�ximo de �ndices compostos 32 foi alcan�ado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[6], "Nome da tabela muito grande: deve ser <= 23")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[7], "O n�mero m�ximo de campos na cl�usula SELECT foi excedido.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[8],"O n�mero m�ximo de campos na cl�usula 'ORDER BY/GROUP BY' foi excedido.")); - - // Column errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[9], "Coluna desconhecida %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[10], "Nome de coluna inv�lido: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[11], "N�mero de coluna inv�lido: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[12], "A(s) coluna(s) a seguir n�o tem (t�m) um ind�ce associado %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[13], "Nome de coluna amb�guo: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[14], "Coluna n�o encontrada: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[15], "Nome de coluna duplicado: %s.")); - - // Primary key errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[16], "Uma chave prim�ria j� foi definida para esta tabela.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[17], "Tabela n�o tem chave prim�ria.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[18], "Comando cria uma chave prim�ria duplicada em %s.")); - - // Type errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[19], "Tipos incompativeis.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[20], "Tamanho do campo deve ser um valor inteiro positivo.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[21], "O valor %s n�o � um n�mero v�lido para o tipo desejado: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[22], "Tipo de dados incompat�vel para a chamada de fun��o: %s")); - - // Number of fields errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[23], "O n�mero de campos � diferente do n�mero de valores ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[24], "O n�mero de valores dado n�o coincide com a defini��o da tabela %d.")); - - // Default value errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[25], "Tamanho do valor padr�o � maior que o tamanho definido para a coluna.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[26], "Uma coluna adicionada declarada como NOT NULL deve ter um valor padr�o n�o nulo.")); - - // Driver errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[27], - "Esta inst�ncia do driver est� fechada e n�o pode ser mais utilizada. Por favor, obtenha uma nova inst�ncia.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[28], "ResultSet j� est� fechado!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[29], "ResultSetMetaData n�o pode ser usado depois que o ResultSet estiver fechado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[30], "O id da aplica��o de ter 4 characteres.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[31], "O incremento deve ser maior do que 0 ou -1.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[32], "Iterador j� foi fechado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[33], "Prepared statement fechado. Por favor, prepare-o novamente.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[34], "Par�metro de conex�o inv�lido: %s.")); - - // Table errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[35], "Nome da tabela n�o encontrado: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[36], "Tabela j� existe: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[37], "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de strings diferente.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[38], "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de criptografia diferente.")); - - // ROWID errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[39], "ROWID n�o pode ser mudado pelo usu�rio!")); - - // Prepared Statement errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[40], "Comando SQL n�o retorna um ResultSet.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[41], "Comando SQL n�o executa uma atualiza��o no banco de dados.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[42], "Nem todos os par�metros da consulta tiveram seus valores definidos.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[43], "N�o foi definido um valor para o par�metro %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[44], "Invalid parameter index.")); - - // Rename errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[45], "N�o � poss�vel renomear a tabela. Esta tabela j� existe: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[46], "Coluna j� existe: %s.")); - - // Alias errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[47], "Nome de tabela/alias repetido: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[48], "Este alias j� est� sendo utilizado no sql: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[49], "Um alias � necess�rio para colunas com fun��o de agrega��o.")); - - // Litebase.execute() error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[50], "Apenas CREATE TABLE e CREATE INDEX s�o permitidos no Litebase.execute()")); - - // Order by and group by errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[51], "Cl�usulas ORDER BY e GROUP BY devem coincidir.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[52], "SQL com cl�usula GROUP BY n�o tem suporte para colunas virtuais.")); - - // Function errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[53], - "Todas colunas que n�os�o fun��es de agrega��o na cl�usula SELECT devem estar na cl�usula GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[54], - "%s n�o � uma fun��o de agrega��o. Todos as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[55], "N�o � possivel misturar colunas reais e de agrega��o no SELECT sem cl�usula GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[56], "N�o � poss�vel ter fun��es de agrega��o com cl�usula ORDER BY sem cl�usula GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[57], - "%s n�o foi listado no SELECT. Todas as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[58], "Fun��es de agrega��o SUM e AVG n�o s�o usadas com colunas do tipo DATE e DATETIME.")); - - // DATE and DATETIME errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[59], "Valor n�o � um tipo DATE v�lido: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[60], "Valor n�o � um tipo DATETIME v�lido: %s.")); - - // Index error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[61], "�ndice j� criado para a coluna %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[62], "N�o � poss�vel remover uma chave prim�ria usando drop index.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[63], "�ndice muito grande. Ele n�o pode ter mais do que 65534 n�s.")); - - // NOT NULL errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[64], "Chave prim�ria n�o pode ter NULL.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[65], "Coluna n�o pode ser NULL: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[66], "Um par�metro em uma where clause n�o pode ser NULL.")); - - // Result set errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[67], "ResultSet em uma posi��o de registro inv�lida %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[68], "Valor inv�lido para casas decimais: %d. Deve ficar entre - 1 e 40.")); - - // File errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[69], "N�o � poss�vel ler da tabela %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[70], "N�o � poss�vel carregar n� folha!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[71], "Tabela est� corrompida: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[72], "Tabela n�o foi fechada corretamente: %s.")); // juliana@220_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[73], "Uma tabela fechada corretamente n�o pode ser usada no recoverTable(): %s.")); // juliana@222_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[74], "N�o � poss�vel achar a posi��o de registro no �ndice na exclus�o.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[75], - "O formato de tabela (%d) n�o � compat�vel com a vers�o do Litebase. Por favor, atualize suas tabelas.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[76], "O formato de tabela n�o � o anterior: %s.")); // juliana@220_11 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[77], "Caminho inv�lido: %s.")); // juliana@214_1 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[78], "Posi��o inv�lida no arquivo: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[79], "Base de dados n�o encontrada.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[80], "Uma tabela aberta n�o pode ser recuperada ou convertida: %s.")); - - // BLOB errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[81], "O tamanho total de um BLOB n�o pode ser maior do que 10 Mb.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[82], "O multiplicador de tamanho n�o � v�lido.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[83], "Um tipo BLOB n�o pode ser parte de uma chave prim�ria.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[84], "Uma coluna do tipo BLOB n�o pode ser indexada.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[85], "Um BLOB n�o pode estar na cl�usula WHERE.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[86], "Um BLOB n�o pode ser convertido em uma string.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[87], "Tipos BLOB n�o podem estar em cl�usulas ORDER BY ou GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[88], "N�o � poss�vel comparar BLOBs.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[89], "S� � poss�vel inserir ou atualizar um BLOB atrav�s prepared statements usando setBlob().")); - - // Lex structures. - while (++i < '[') // The values for the letters. - { - ASSERT2_EQUALS(I32, is[i] & IS_ALPHA, IS_ALPHA); - ASSERT2_EQUALS(I32, is[i + 32] & IS_ALPHA, IS_ALPHA); - } - - // Letters denoting types of numbers can also be the end of the number. - ASSERT2_EQUALS(I32, is['d'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['D'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['f'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['F'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['l'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['L'] & IS_END_NUM, IS_END_NUM); - - // The values for the digits. - i = '/'; - while (++i < ':') - ASSERT2_EQUALS(U8, is[i], IS_DIGIT); - - ASSERT2_EQUALS(I32, is['_'] & (IS_ALPHA | IS_DIGIT), (IS_ALPHA | IS_DIGIT)); // '_' can be part of an identifier. - - // + and - can be operators or sign of numbers. - ASSERT2_EQUALS(U8, is['+'], IS_SIGN); - ASSERT2_EQUALS(U8, is['-'], IS_SIGN); - - // The other operators and brackets. - ASSERT2_EQUALS(U8, is['*'], IS_OPERATOR); - ASSERT2_EQUALS(U8, is['('], IS_OPERATOR); - ASSERT2_EQUALS(U8, is[')'], IS_OPERATOR); - - // The symbols that can represent double tokens. - ASSERT2_EQUALS(U8, is['<'], IS_RELATIONAL); - ASSERT2_EQUALS(U8, is['>'], IS_RELATIONAL); - ASSERT2_EQUALS(U8, is['!'], IS_RELATIONAL); - - // The symbols that are treated as punctuators and =. - ASSERT2_EQUALS(U8, is['.'], IS_PUNCT); - ASSERT2_EQUALS(U8, is[','], IS_PUNCT); - ASSERT2_EQUALS(U8, is['?'], IS_PUNCT); - ASSERT2_EQUALS(U8, is['='], IS_PUNCT); - - // Checks if the hash codes of the reserved words are in the hash table. - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("abs")), TK_ABS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("add")), TK_ADD); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("alter")), TK_ALTER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("and")), TK_AND); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("as")), TK_AS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("asc")), TK_ASC); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("avg")), TK_AVG); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("blob")), TK_BLOB); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("by")), TK_BY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("char")), TK_CHAR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("count")), TK_COUNT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("create")), TK_CREATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("date")), TK_DATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("datetime")), TK_DATETIME); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("day")), TK_DAY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("default")), TK_DEFAULT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("delete")), TK_DELETE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("desc")), TK_DESC); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("distinct")), TK_DISTINCT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("double")), TK_DOUBLE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("drop")), TK_DROP); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("float")), TK_FLOAT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("from")), TK_FROM); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("group")), TK_GROUP); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("having")), TK_HAVING); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("hour")), TK_HOUR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("index")), TK_INDEX); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("insert")), TK_INSERT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("int")), TK_INT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("into")), TK_INTO); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("is")), TK_IS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("key")), TK_KEY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("like")), TK_LIKE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("long")), TK_LONG); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("lower")), TK_LOWER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("max")), TK_MAX); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("millis")), TK_MILLIS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("min")), TK_MIN); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("minute")), TK_MINUTE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("month")), TK_MONTH); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("nocase")), TK_NOCASE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("not")), TK_NOT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("null")), TK_NULL) ; - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("on")), TK_ON); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("or")), TK_OR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("order")), TK_ORDER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("primary")), TK_PRIMARY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("rename")), TK_RENAME); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("second")), TK_SECOND); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("select")), TK_SELECT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("set")), TK_SET); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("short")), TK_SHORT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("sum")), TK_SUM); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("table")), TK_TABLE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("to")), TK_TO); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("update")), TK_UPDATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("upper")), TK_UPPER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("values")), TK_VALUES); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("varchar")), TK_VARCHAR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("where")), TK_WHERE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("year")), TK_YEAR); - - // Classes. - ASSERT1_EQUALS(NotNull, litebaseConnectionClass); - ASSERT1_EQUALS(NotNull, loggerClass); - - ASSERT1_EQUALS(True, ranTests); // Enables the test cases. - -finish: ; -} - -/** - * Tests the function bindFunctionDataType() works properly. It is tested with all possible data types and data function types, including - * invalid values. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(bindFunctionDataType) -{ - UNUSED(currentContext) - - // Tests UNDEFINED type, which cannot be bound. - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(UNDEFINED_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests CHAR type, which is only used for UPPER and LOWER. - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(True, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(True, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests SHORT type, which is only used for ABS. - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(True, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(SHORT_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests INT type, which is only used for ABS. - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(True, bindFunctionDataType(INT_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(INT_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests LONG type, which is only used for ABS. - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(True, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(LONG_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests FLOAT type, which is only used for ABS. - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(True, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(FLOAT_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests DOUBLE type, which is only used for ABS. - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(True, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(DOUBLE_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests CHARS_NOCASE type, which is only used for UPPER and LOWER. - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(True, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(True, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(CHARS_NOCASE_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests BOOLEAN type, which cannot be bound. - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(BOOLEAN_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests DATE type, which is only used for YEAR, MONTH, and DAY. - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATE_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests DATETIME type, which is only used for YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, and MILLIS. - ASSERT1_EQUALS(False, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(True, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(DATETIME_TYPE, FUNCTION_DT_LOWER + 1)); - - // Tests BLOB type, which cannot be bound. - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_NONE)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_UPPER)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_LOWER)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_ABS)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_YEAR)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_MONTH)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_DAY)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_HOUR)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_MINUTE)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_SECOND)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_MILLIS)); - ASSERT1_EQUALS(False, bindFunctionDataType(BLOB_TYPE, FUNCTION_DT_LOWER + 1)); - -finish: ; -} - -/** - * Tests if the function checkApppath() accepts paths if and only if they are valid. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(checkApppath) -{ - char sourcePath[MAX_PATHNAME]; - char defaultAppPath[MAX_PATHNAME]; - char path[300]; - int32 len; - - TC_getDataPath(defaultAppPath); - if (!defaultAppPath[0]) - xstrcpy(defaultAppPath, TC_getAppPath()); - len = xstrlen(defaultAppPath); - if (defaultAppPath[len - 1] != '\\' && defaultAppPath[len - 1] != '/') - xstrcat(defaultAppPath, "/"); - - // null. - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, null)); - ASSERT2_EQUALS(Sz, sourcePath, defaultAppPath); - - // Empty string. - path[0] = 0; - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - - // Just spaces. - xstrcpy(path, " "); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, " "); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - - // Relative paths. - xstrcpy(path, "."); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, ".."); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, "./"); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, "../"); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, "/."); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, "/.."); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - xstrcpy(path, "/Litebase/../tables/"); - ASSERT1_EQUALS(False, checkApppath(currentContext, sourcePath, path)); - - // Correct paths. - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, defaultAppPath)); - ASSERT2_EQUALS(Sz, sourcePath, defaultAppPath); - xstrcpy(path, "/"); - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, path)); - ASSERT2_EQUALS(Sz, sourcePath, "/"); - xstrcpy(path, "\\"); - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, path)); - ASSERT2_EQUALS(Sz, sourcePath, "/"); -#if !defined(POSIX) && !defined(ANDROID) - xstrcpy(path, "\\temp\\tables"); - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, path)); - ASSERT2_EQUALS(Sz, sourcePath, "/temp/tables/"); - xstrcpy(path, "/temp/tables/"); - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, path)); - ASSERT2_EQUALS(Sz, sourcePath, "/temp/tables/"); -#endif -#ifdef WIN32 - xstrcpy(path, "p:\\temp\\tables/"); - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, path)); - ASSERT2_EQUALS(Sz, sourcePath, "p:/temp/tables/"); - xstrcpy(path, "p:/temp/tables/"); - ASSERT1_EQUALS(True, checkApppath(currentContext, sourcePath, path)); - ASSERT2_EQUALS(Sz, sourcePath, "p:/temp/tables/"); -#endif - -finish: ; -} - -/** - * Tests if the function dataTypeFunctionsName() returns the strings with the correct types. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(dataTypeFunctionsName) -{ - UNUSED(currentContext) - ASSERT2_EQUALS(Sz, "", dataTypeFunctionsName(FUNCTION_DT_NONE)); - ASSERT2_EQUALS(Sz, "year", dataTypeFunctionsName(FUNCTION_DT_YEAR)); - ASSERT2_EQUALS(Sz, "month", dataTypeFunctionsName(FUNCTION_DT_MONTH)); - ASSERT2_EQUALS(Sz, "day", dataTypeFunctionsName(FUNCTION_DT_DAY)); - ASSERT2_EQUALS(Sz, "hour", dataTypeFunctionsName(FUNCTION_DT_HOUR)); - ASSERT2_EQUALS(Sz, "minute", dataTypeFunctionsName(FUNCTION_DT_MINUTE)); - ASSERT2_EQUALS(Sz, "second", dataTypeFunctionsName(FUNCTION_DT_SECOND)); - ASSERT2_EQUALS(Sz, "millis", dataTypeFunctionsName(FUNCTION_DT_MILLIS)); - ASSERT2_EQUALS(Sz, "abs", dataTypeFunctionsName(FUNCTION_DT_ABS)); - ASSERT2_EQUALS(Sz, "upper", dataTypeFunctionsName(FUNCTION_DT_UPPER)); - ASSERT2_EQUALS(Sz, "lower", dataTypeFunctionsName(FUNCTION_DT_LOWER)); - ASSERT2_EQUALS(Sz, "", dataTypeFunctionsName(FUNCTION_DT_LOWER + 1)); - -finish: ; -} - -/** - * Tests if initVars() initializes all the needed variables. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(initVars) -{ - int32 i = '@'; - UNUSED(currentContext) - - // The TCVM functions needed by Litebase. - ASSERT1_EQUALS(NotNull, TC_CharP2JCharP); - ASSERT1_EQUALS(NotNull, TC_CharP2JCharPBuf); - ASSERT1_EQUALS(NotNull, TC_CharP2TCHARPBuf); - ASSERT1_EQUALS(NotNull, TC_CharPToLower); - ASSERT1_EQUALS(NotNull, TC_JCharP2CharP); - ASSERT1_EQUALS(NotNull, TC_JCharP2CharPBuf); - ASSERT1_EQUALS(NotNull, TC_JCharP2TCHARPBuf); - ASSERT1_EQUALS(NotNull, TC_JCharPEqualsJCharP); - ASSERT1_EQUALS(NotNull, TC_JCharPEqualsIgnoreCaseJCharP); - ASSERT1_EQUALS(NotNull, TC_JCharPHashCode); - ASSERT1_EQUALS(NotNull, TC_JCharPIndexOfJChar); - ASSERT1_EQUALS(NotNull, TC_JCharPLen); - ASSERT1_EQUALS(NotNull, TC_JCharToLower); - ASSERT1_EQUALS(NotNull, TC_JCharToUpper); - ASSERT1_EQUALS(NotNull, TC_TCHARP2CharPBuf); - ASSERT1_EQUALS(NotNull, TC_alert); - ASSERT1_EQUALS(NotNull, TC_appendCharP); // juliana@230_30 - ASSERT1_EQUALS(NotNull, TC_appendJCharP); // juliana@230_30 - ASSERT1_EQUALS(NotNull, TC_areClassesCompatible); - ASSERT1_EQUALS(NotNull, TC_createArrayObject); - ASSERT1_EQUALS(NotNull, TC_createObject); - ASSERT1_EQUALS(NotNull, TC_createStringObjectFromCharP); - ASSERT1_EQUALS(NotNull, TC_createStringObjectFromTCHARP); - ASSERT1_EQUALS(NotNull, TC_createStringObjectWithLen); - ASSERT1_EQUALS(NotNull, TC_debug); - ASSERT1_EQUALS(NotNull, TC_double2str); - ASSERT1_EQUALS(NotNull, TC_executeMethod); - ASSERT1_EQUALS(NotNull, TC_getApplicationId); - ASSERT1_EQUALS(NotNull, TC_getAppPath); - ASSERT1_EQUALS(NotNull, TC_getDataPath); - ASSERT1_EQUALS(NotNull, TC_getDateTime); - ASSERT1_EQUALS(NotNull, TC_getErrorMessage); - ASSERT1_EQUALS(NotNull, TC_getSettingsPtr); - ASSERT1_EQUALS(NotNull, TC_getTimeStamp); - ASSERT1_EQUALS(NotNull, TC_hashCode); - ASSERT1_EQUALS(NotNull, TC_hashCodeFmt); - ASSERT1_EQUALS(NotNull, TC_heapAlloc); - ASSERT1_EQUALS(NotNull, TC_heapDestroyPrivate); - ASSERT1_EQUALS(NotNull, TC_hstrdup); - ASSERT1_EQUALS(NotNull, TC_htFree); - ASSERT1_EQUALS(NotNull, TC_htFreeContext); - ASSERT1_EQUALS(NotNull, TC_htGet32); - ASSERT1_EQUALS(NotNull, TC_htGet32Inv); - ASSERT1_EQUALS(NotNull, TC_htGetPtr); - ASSERT1_EQUALS(NotNull, TC_htNew); - ASSERT1_EQUALS(NotNull, TC_htPut32); - ASSERT1_EQUALS(NotNull, TC_htPut32IfNew); - ASSERT1_EQUALS(NotNull, TC_htPutPtr); - ASSERT1_EQUALS(NotNull, TC_htRemove); - ASSERT1_EQUALS(NotNull, TC_int2CRID); - ASSERT1_EQUALS(NotNull, TC_int2str); - ASSERT1_EQUALS(NotNull, TC_listFiles); - ASSERT1_EQUALS(NotNull, TC_loadClass); - ASSERT1_EQUALS(NotNull, TC_long2str); - ASSERT1_EQUALS(NotNull, TC_privateHeapCreate); - ASSERT1_EQUALS(NotNull, TC_privateHeapSetJump); - ASSERT1_EQUALS(NotNull, TC_privateXfree); - ASSERT1_EQUALS(NotNull, TC_privateXmalloc); - ASSERT1_EQUALS(NotNull, TC_privateXrealloc); - ASSERT1_EQUALS(NotNull, TC_setObjectLock); - ASSERT1_EQUALS(NotNull, TC_str2double); - ASSERT1_EQUALS(NotNull, TC_str2int); - ASSERT1_EQUALS(NotNull, TC_str2long); - ASSERT1_EQUALS(NotNull, TC_throwExceptionNamed); - ASSERT1_EQUALS(NotNull, TC_throwNullArgumentException); - ASSERT1_EQUALS(NotNull, TC_tiF_create_sii); - ASSERT1_EQUALS(NotNull, TC_toLower); - ASSERT1_EQUALS(NotNull, TC_trace); - ASSERT1_EQUALS(NotNull, TC_validatePath); // juliana@214_1 - -#ifdef ENABLE_MEMORY_TEST - ASSERT1_EQUALS(NotNull, TC_getCountToReturnNull); - ASSERT1_EQUALS(NotNull, TC_setCountToReturnNull); -#endif - - // A hash table for the loaded connections. - ASSERT1_EQUALS(NotNull, htCreatedDrivers.items); - ASSERT1_EQUALS(Null, htCreatedDrivers.heap); - ASSERT2_EQUALS(I32, htCreatedDrivers.size, 0); - ASSERT2_EQUALS(I32, htCreatedDrivers.hash, 9); - ASSERT2_EQUALS(I32, htCreatedDrivers.threshold, 10); - - // A hash table for select statistics. - ASSERT1_EQUALS(NotNull, memoryUsage.items); - ASSERT2_EQUALS(I32, memoryUsage.size, 0); - ASSERT2_EQUALS(I32, memoryUsage.hash, 99); - ASSERT2_EQUALS(I32, memoryUsage.threshold, 100); - - // Error messages. - // English messages. - // General errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[0], "Error: ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[1], " Near position %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[2], "Syntax error.")); - - // Limit errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[3], "Maximum number of different fields was reached.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[4], "Maximum number of parameters in the 'WHERE/HAVING' clause was reached.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[5], "Maximum number of composed indices 32 was reached.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[6], "Table name too big: must be <= 23.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[7], "The maximum number of fields in a SELECT clause was exceeded.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[8], "Maximum number of columns exceeded in the 'ORDER BY/GROUP BY' clause.")); - - // Column errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[9], "Unknown column %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[10], "Invalid column name: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[11], "Invalid column number: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[12], "The following column(s) does (do) not have an associated index %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[13], "Column name in field list is ambiguous: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[14], "Column not found %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[15], "Duplicated column name: %s.")); - - // Primary key errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[16], "A primary key was already defined for this table.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[17], "Table does not have a primary key.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[18], "Statement creates a duplicated primary key in %s.")); - - // Type errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[19], "Incompatible types.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[20], "Field size must be a positive interger value.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[21], "Value %s is not a valid number for the desired type: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[22], "Incompatible data type for the function call: %s")); - - // Number of fields errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[23], "The number of fields does not match the number of values ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[24], "The given number of values does not match the table definition %d.")); - - // Default value errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[25], "Length of default value is bigger than column size.")); - - // Driver errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[26], "This driver instance was closed and cannot be used anymore. Please get a new instance of it.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[27], "ResultSet already closed!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[28], "ResultSetMetaData cannot be used after the ResultSet is closed.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[29], "The application id must be four characters long.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[30], "The increment must be greater than 0 or -1.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[31], "Iterator already closed.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[32], "Prepared statement closed. Please prepare it again.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[33], "Invalid connection parameter: %s.")); - - // Table errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[34], "Table name not found: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[35], "Table already created: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[36], "It is not possible to open a table within a connection with a different string format.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[37], "It is not possible to open a table within a connection with a different cryptography format.")); - - // ROWID errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[38], "ROWID can't be changed by the user!")); - - // Prepared Statement errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[39], "Query does not return result set.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[40], "Query does not perform updates in the database.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[41], "Not all parameters of the query had their values defined.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[42], "A value was not defined for the parameter %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[43], "Invalid parameter index.")); - - // Rename errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[44], "Can't rename table. This table already exists: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[45], "Column already exists: %s.")); - - // Alias errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[46], "Not unique table/alias: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[47], "This alias is already being used in this expression: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[48], "An alias is required for the aggregate function column.")); - - // Litebase.execute() error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[49], "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute().")); - - // Order by and group by errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[50], "ORDER BY and GROUP BY clauses must match.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[51], "No support for virtual columns in SQL queries with GROUP BY clause.")); - - // Function errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[52], "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[53], - "%s is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation functions.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[54], - "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[55], "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[56], -"%s was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation funtions." -)); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[57], "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields.")); - - // DATE and DATETIME errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[58], "Value is not a DATE: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[59], "Value is not a DATETIME: %s.")); - - // Index error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[60], "Index already created for column %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[61], "Can't drop a primary key index withdrop index.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[62], "Index too large. It can't have more than 65534 nodes.")); - - // NOT NULL errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[63], "Primary key can't have null.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[64], "Field can't be null: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[65], "A parameter in a where clause can't be null.")); - - // Result set errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[66], "ResultSet in invalid record position: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[67], "Invalid value for decimal places: %d. It must range from -1 to 40.")); - - // File errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[68], "Can't read from table %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[69], "Can't load leaf node!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[70], "Table is corrupted: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[71], "Table not closed properly: %s.")); // juliana@220_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[72], "A properly closed table can't be used in recoverTable(): %s.")); // juliana@222_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[73], "Can't find index record position on delete.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[74], "The table format (%d) is incompatible with Litebase version. Please update your tables.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[75], "The table format is not the previous one: %s.")); // juliana@220_11 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[76], "Invalid path: %s.")); // juliana@214_1 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[77], "Invalid file position: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[78], "Database not found.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[79], "An opened table can't be recovered or converted: %s.")); - - // BLOB errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[80], "The total size of a blob can't be greater then 10 Mb.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[81], "This is not a valid size multiplier.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[82], "A blob type can't be part of a primary key.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[83], "A BLOB column can't be indexed.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[84], "A BLOB can't be in the where clause.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[85], "A BLOB can't be converted to a string.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[86], "Blobs types can't be in ORDER BY or GROUP BY clauses.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[87], "It is not possible to compare BLOBs.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_en[88], "It is only possible to insert or update a BLOB through prepared statements using setBlob().")); - - // Portuguese messages. - // General errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[0], "Erro: ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[1], " Pr�ximo � posi��o %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[2], "Erro de sintaxe.")); - - // Limit errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[3], "N�mero m�ximo de campos diferentes foi alcan�ado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[4], "N�mero m�ximo da lista de par�metros na cl�usula 'WHERE/HAVING' foi alcan�ado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[5], "Numero m�ximo de �ndices compostos 32 foi alcan�ado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[6], "Nome da tabela muito grande: deve ser <= 23")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[7], "O n�mero m�ximo de campos na cl�usula SELECT foi excedido.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[8],"O n�mero m�ximo de campos na cl�usula 'ORDER BY/GROUP BY' foi excedido.")); - - // Column errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[9], "Coluna desconhecida %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[10], "Nome de coluna inv�lido: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[11], "N�mero de coluna inv�lido: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[12], "A(s) coluna(s) a seguir n�o tem (t�m) um ind�ce associado %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[13], "Nome de coluna amb�guo: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[14], "Coluna n�o encontrada: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[15], "Nome de coluna duplicado: %s.")); - - // Primary key errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[16], "Uma chave prim�ria j� foi definida para esta tabela.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[17], "Tabela n�o tem chave prim�ria.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[18], "Comando cria uma chave prim�ria duplicada em %s.")); - - // Type errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[19], "Tipos incompativeis.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[20], "Tamanho do campo deve ser um valor inteiro positivo.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[21], "O valor %s n�o � um n�mero v�lido para o tipo desejado: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[22], "Tipo de dados incompat�vel para a chamada de fun��o: %s")); - - // Number of fields errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[23], "O n�mero de campos � diferente do n�mero de valores ")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[24], "O n�mero de valores dado n�o coincide com a defini��o da tabela %d.")); - - // Default value errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[25], "Tamanho do valor padr�o � maior que o tamanho definido para a coluna.")); - - // Driver errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[26], - "Esta inst�ncia do driver est� fechada e n�o pode ser mais utilizada. Por favor, obtenha uma nova inst�ncia.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[27], "ResultSet j� est� fechado!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[28], "ResultSetMetaData n�o pode ser usado depois que o ResultSet estiver fechado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[29], "O id da aplica��o de ter 4 characteres.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[30], "O incremento deve ser maior do que 0 ou -1.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[31], "Iterador j� foi fechado.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[32], "Prepared statement fechado. Por favor, prepare-o novamente.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[33], "Par�metro de conex�o inv�lido: %s.")); - - // Table errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[34], "Nome da tabela n�o encontrado: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[35], "Tabela j� existe: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[36], "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de strings diferente.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[37], "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de criptografia diferente.")); - - // ROWID errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[38], "ROWID n�o pode ser mudado pelo usu�rio!")); - - // Prepared Statement errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[39], "Comando SQL n�o retorna um ResultSet.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[40], "Comando SQL n�o executa uma atualiza��o no banco de dados.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[41], "Nem todos os par�metros da consulta tiveram seus valores definidos.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[42], "N�o foi definido um valor para o par�metro %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[43], "Invalid parameter index.")); - - // Rename errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[44], "N�o � poss�vel renomear a tabela. Esta tabela j� existe: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[45], "Coluna j� existe: %s.")); - - // Alias errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[46], "Nome de tabela/alias repetido: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[47], "Este alias j� est� sendo utilizado no sql: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[48], "Um alias � necess�rio para colunas com fun��o de agrega��o.")); - - // Litebase.execute() error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[49], "Apenas CREATE TABLE e CREATE INDEX s�o permitidos no Litebase.execute()")); - - // Order by and group by errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[50], "Cl�usulas ORDER BY e GROUP BY devem coincidir.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[51], "SQL com cl�usula GROUP BY n�o tem suporte para colunas virtuais.")); - - // Function errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[52], - "Todas colunas que n�os�o fun��es de agrega��o na cl�usula SELECT devem estar na cl�usula GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[53], - "%s n�o � uma fun��o de agrega��o. Todos as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[54], "N�o � possivel misturar colunas reais e de agrega��o no SELECT sem cl�usula GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[55], "N�o � poss�vel ter fun��es de agrega��o com cl�usula ORDER BY sem cl�usula GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[56], - "%s n�o foi listado no SELECT. Todas as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[57], "Fun��es de agrega��o SUM e AVG n�o s�o usadas com colunas do tipo DATE e DATETIME.")); - - // DATE and DATETIME errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[58], "Valor n�o � um tipo DATE v�lido: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[59], "Valor n�o � um tipo DATETIME v�lido: %s.")); - - // Index error. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[60], "�ndice j� criado para a coluna %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[61], "N�o � poss�vel remover uma chave prim�ria usando drop index.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[62], "�ndice muito grande. Ele n�o pode ter mais do que 65534 n�s.")); - - // NOT NULL errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[63], "Chave prim�ria n�o pode ter NULL.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[64], "Coluna n�o pode ser NULL: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[65], "Um par�metro em uma where clause n�o pode ser NULL.")); - - // Result set errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[66], "ResultSet em uma posi��o de registro inv�lida %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[67], "Valor inv�lido para casas decimais: %d. Deve ficar entre - 1 e 40.")); - - // File errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[68], "N�o � poss�vel ler da tabela %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[69], "N�o � poss�vel carregar n� folha!")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[70], "Tabela est� corrompida: %s.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[71], "Tabela n�o foi fechada corretamente: %s.")); // juliana@220_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[72], "Uma tabela fechada corretamente n�o pode ser usada no recoverTable(): %s.")); // juliana@222_2 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[73], "N�o � poss�vel achar a posi��o de registro no �ndice na exclus�o.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[74], - "O formato de tabela (%d) n�o � compat�vel com a vers�o do Litebase. Por favor, atualize suas tabelas.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[75], "O formato de tabela n�o � o anterior: %s.")); // juliana@220_11 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[76], "Caminho inv�lido: %s.")); // juliana@214_1 - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[77], "Posi��o inv�lida no arquivo: %d.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[78], "Base de dados n�o encontrada.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[79], "Uma tabela aberta n�o pode ser recuperada ou convertida: %s.")); - - // BLOB errors. - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[80], "O tamanho total de um BLOB n�o pode ser maior do que 10 Mb.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[81], "O multiplicador de tamanho n�o � v�lido.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[82], "Um tipo BLOB n�o pode ser parte de uma chave prim�ria.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[83], "Uma coluna do tipo BLOB n�o pode ser indexada.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[84], "Um BLOB n�o pode estar na cl�usula WHERE.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[85], "Um BLOB n�o pode ser convertido em uma string.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[86], "Tipos BLOB n�o podem estar em cl�usulas ORDER BY ou GROUP BY.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[87], "N�o � poss�vel comparar BLOBs.")); - ASSERT1_EQUALS(False, xstrcmp(errorMsgs_pt[88], "S� � poss�vel inserir ou atualizar um BLOB atrav�s prepared statements usando setBlob().")); - - // Lex structures. - while (++i < '[') // The values for the letters. - { - ASSERT2_EQUALS(I32, is[i] & IS_ALPHA, IS_ALPHA); - ASSERT2_EQUALS(I32, is[i + 32] & IS_ALPHA, IS_ALPHA); - } - - // Letters denoting types of numbers can also be the end of the number. - ASSERT2_EQUALS(I32, is['d'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['D'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['f'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['F'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['l'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['L'] & IS_END_NUM, IS_END_NUM); - - // The values for the digits. - i = '/'; - while (++i < ':') - ASSERT2_EQUALS(U8, is[i], IS_DIGIT); - - ASSERT2_EQUALS(I32, is['_'] & (IS_ALPHA | IS_DIGIT), (IS_ALPHA | IS_DIGIT)); // '_' can be part of an identifier. - - // + and - can be operators or sign of numbers. - ASSERT2_EQUALS(U8, is['+'], IS_SIGN); - ASSERT2_EQUALS(U8, is['-'], IS_SIGN); - - // The other operators and brackets. - ASSERT2_EQUALS(U8, is['*'], IS_OPERATOR); - ASSERT2_EQUALS(U8, is['('], IS_OPERATOR); - ASSERT2_EQUALS(U8, is[')'], IS_OPERATOR); - - // The symbols that can represent double tokens. - ASSERT2_EQUALS(U8, is['<'], IS_RELATIONAL); - ASSERT2_EQUALS(U8, is['>'], IS_RELATIONAL); - ASSERT2_EQUALS(U8, is['!'], IS_RELATIONAL); - - // The symbols that are treated as punctuators and =. - ASSERT2_EQUALS(U8, is['.'], IS_PUNCT); - ASSERT2_EQUALS(U8, is[','], IS_PUNCT); - ASSERT2_EQUALS(U8, is['?'], IS_PUNCT); - ASSERT2_EQUALS(U8, is['='], IS_PUNCT); - - // Checks if the hash codes of the reserved words are in the hash table. - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("abs")), TK_ABS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("add")), TK_ADD); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("alter")), TK_ALTER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("and")), TK_AND); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("as")), TK_AS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("asc")), TK_ASC); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("avg")), TK_AVG); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("blob")), TK_BLOB); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("by")), TK_BY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("char")), TK_CHAR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("count")), TK_COUNT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("create")), TK_CREATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("date")), TK_DATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("datetime")), TK_DATETIME); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("day")), TK_DAY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("default")), TK_DEFAULT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("delete")), TK_DELETE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("desc")), TK_DESC); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("distinct")), TK_DISTINCT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("double")), TK_DOUBLE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("drop")), TK_DROP); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("float")), TK_FLOAT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("from")), TK_FROM); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("group")), TK_GROUP); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("having")), TK_HAVING); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("hour")), TK_HOUR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("index")), TK_INDEX); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("insert")), TK_INSERT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("int")), TK_INT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("into")), TK_INTO); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("is")), TK_IS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("key")), TK_KEY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("like")), TK_LIKE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("long")), TK_LONG); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("lower")), TK_LOWER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("max")), TK_MAX); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("millis")), TK_MILLIS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("min")), TK_MIN); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("minute")), TK_MINUTE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("month")), TK_MONTH); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("nocase")), TK_NOCASE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("not")), TK_NOT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("null")), TK_NULL) ; - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("on")), TK_ON); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("or")), TK_OR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("order")), TK_ORDER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("primary")), TK_PRIMARY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("rename")), TK_RENAME); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("second")), TK_SECOND); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("select")), TK_SELECT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("set")), TK_SET); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("short")), TK_SHORT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("sum")), TK_SUM); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("table")), TK_TABLE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("to")), TK_TO); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("update")), TK_UPDATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("upper")), TK_UPPER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("values")), TK_VALUES); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("varchar")), TK_VARCHAR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("where")), TK_WHERE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("year")), TK_YEAR); - - // Classes. - ASSERT1_EQUALS(NotNull, litebaseConnectionClass); - ASSERT1_EQUALS(NotNull, loggerClass); - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/Litebase.h b/LitebaseSDK/src/native/Litebase.h deleted file mode 100644 index 83cc2862c7..0000000000 --- a/LitebaseSDK/src/native/Litebase.h +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions to deal with important Litebase funcionalities. - */ - -#ifdef WP8 -#define inline -#endif - -#ifndef LITEBASE_H -#define LITEBASE_H - -#include "tcvm.h" -#include "File.h" -#include "StringBuffer.h" -#include "lbFile.h" - -#include "Constants.h" -#include "LitebaseGlobals.h" -#include "Key.h" -#include "Index.h" -#include "LitebaseLex.h" -#include "LitebaseMessage.h" -#include "LitebaseParser.h" -#include "LitebaseTypes.h" -#include "MarkBits.h" -#include "MemoryFile.h" -#include "Node.h" -#include "NormalFile.h" -#include "PlainDB.h" -#include "PreparedStatement.h" -#include "ResultSet.h" -#include "SQLBooleanClause.h" -#include "SQLBooleanClauseTree.h" -#include "SQLColumnListClause.h" -#include "SQLDeleteStatement.h" -#include "SQLInsertStatement.h" -#include "SQLSelectStatement.h" -#include "SQLUpdateStatement.h" -#include "SQLValue.h" -#include "Table.h" -#include "TCVMLib.h" -#include "UtilsLB.h" - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * Loads the necessary data when using Litebase for the first time. - * - * @param params Some parameters and function pointers in order to load a .dll. - * @return false if an error occurs; true, otherwise. - */ -LB_API bool LibOpen(OpenParams params); - -/** - * Flushs all pending data and destroy all Litebase structures when closing the application. - */ -LB_API void LibClose(); - -/** - * Loads the necessary data when using Litebase for the first time. - * - * @param params Some parameters and function pointers in order to load a .dll. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool initVars(OpenParams params); - -/** - * Creates a LitebaseConnection for the given creator id and with the given connection param list. This method avoids the creation of more than - * one instance with the same creator id and parameters, which would lead to performance and memory problems. - * - * @param context The thread context where the function is being executed. - * @param crid The creator id, which may be the same one of the current application and MUST be 4 characters long. - * @param objParams Only the folder where it is desired to store the tables, null, if it is desired to use the current data - * path, or chars_type = chars_format; path = source_path[;crypto] , where chars_format can be ascii or - * unicode, source_path is the folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is passed as a parameter, unicode is used and there is no - * cryptography. Notice that path must be absolute, not relative. - *

    Note that databases belonging to multiple applications can be stored in the same path, since all tables are prefixed by the application's - * creator id. - *

    Also notice that to store Litebase files on card on Pocket PC, just set the second parameter to the correct directory path. - *

    It is not recommended to create the databases directly on the PDA. Memory cards are FIVE TIMES SLOWER than the main memory, so it will take - * a long time to create the tables. Even if the NVFS volume is used, it can be very slow. It is better to create the tables on the desktop, and - * copy everything to the memory card or to the NVFS volume. - *

    Due to the slowness of a memory card and the NVFS volume, all queries will be stored in the main memory; only tables and indexes will be - * stored on the card or on the NVFS volume. - *

    An exception will be raised if tables created with an ascii kind of connection are oppened with an unicode connection and vice-versa. - * @return A Litebase instance. - * @throws OutOfMemoryError If memory allocation fails. - */ -TCObject create(Context context, int32 crid, TCObject objParams); - -/** - * Frees all data concerning a certaim driver connection. - * - * @param context The thread context where the function is being executed. - * @param driver The driver as an int because this function may be called from a hash table. - */ -void freeLitebase(Context context, size_t driver); - -/** - * Used to execute a create table or create index SQL commands. - * - *

    Examples: - *

      - *
    • driver.execute("create table PERSON (NAME CHAR(30), SALARY DOUBLE, AGE INT, EMAIL CHAR(50))"); - *
    • driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - *
    - * - *

    When creating an index, its name is ignored but must be given. The index can be created after data was added to the table. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param sqlStr The SQL creation command. - * @param sqlLen The SQL string length. - * @throws SQLParseException If the table name or a default string is too big, there is an invalid default value, or an unknown (on a create table) - * or repeated column name, or an invalid number occurs. - * @throws AlreadyCreatedException If the table or index is already created. - * @throws OutOfMemoryError If a memory allocation fails. - */ -void litebaseExecute(Context context, TCObject driver, JCharP sqlStr, uint32 len); - -/** - * Used to execute updates in a table (insert, delete, update, alter table, drop). E.g.: - * - *

    driver.executeUpdate("drop table person"); will drop also the indices. - *

    driver.executeUpdate("drop index * on person"); will drop all indices but not the primary key index. - *

    driver.executeUpdate("drop index name on person"); will drop the index for the "name" column. - *

    driver.executeUpdate("ALTER TABLE person DROP primary key"); will drop the primary key. - *

    driver.executeUpdate("update person set age=44, salary=3200.5 where name = 'guilherme campos hazan'"); - * will update the table. - *

    driver.executeUpdate("delete person where name like 'g%'"); will delete records of the table. - *

    driver.executeUpdate("insert into person (age, salary, name, email) - * values (32, 2000, 'guilherme campos hazan', 'guich@superwaba.com.br')"); will insert a record in the table. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param sqlStr The SQL creation command. - * @param sqlLen The SQL string length. - * @return The number of rows affected or 0 if a drop or alter operation was successful, or -1 if an error occurs. - * @throws OutOfMemoryError If a memory allocation fails. - */ -int32 litebaseExecuteUpdate(Context context, TCObject driver, JCharP sqlStr, int32 sqlLen); - -/** - * Used to execute queries in a table. Example: - * - *

    - * ResultSet rs = driver.executeQuery("select rowid, name, salary, age from person where age != 44");
    - * rs.afterLast();
    - * while (rs.prev())
    - *    Vm.debug(rs.getString(1) + ". " + rs.getString(2) + " - " + rs.getInt("age") + " years");
    - * 
    - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param sqlStr The SQL creation command. - * @param sqlLen The SQL string length. - * @return A result set with the values returned from the query or null if an error occurs. - * @throws OutOfMemoryError If a memory allocation fails. - */ -TCObject litebaseExecuteQuery(Context context, TCObject driver, JCharP strSql, int32 length); - -/** - * Drops a table. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param parser The parser. - * @throws DriverException If the table does not exist, if its name is greater than the maximum possible or it is not possible to remove it. - */ -void litebaseExecuteDropTable(Context context, TCObject driver, LitebaseParser* parser); - -/** - * Drops an index. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param parser The parser. - * @return -1 if an error occured; the number of indices removed, otherwise. - * @throws DriverException If a column does not have an index, is invalid, or if the columns to have the index dropped are from a primary key. - */ -int32 litebaseExecuteDropIndex(Context context, TCObject driver, LitebaseParser* parser); - -/** - * Executes an alter statement. - * - * @param context The thread context where the function is being executed. - * @param driver The current Litebase connection. - * @param parser The parser. - * @throws DriverException If there is no primary key to be dropped, or an invalid column name. - * @throws AlreadyCreatedException If one tries to add another primary key. - * @throws SQLParseException If there is a blob in a primary key definition or there is a duplicated column name in the primary key definition. - * @throws OutOfMemoryError If a memory allocation fails. - */ -void litebaseExecuteAlter(Context context, TCObject driver, LitebaseParser* parser); - -/** - * Gets the value of a column stored in the row iterator. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The column index. - * @param type The type of the column. - * @param p->retI Receives an int or a short. - * @param p->retL Receives a long. - * @param p->retD Receives a float or a double. - * @param p->retO Receives a string, blob, date, or datetime. - * @throws DriverException If the column is not of type requested. - * @throws IllegalArgumentException If the column index is invalid. - */ -void getByIndex(NMParams p, int32 type); - -/** - * Tests if the row iterator or the driver where it was created is closed. - * - * @param p->obj[0] The row iterator object. - * @throws IllegalStateException If the row iterator or driver is closed. - */ -bool testRIClosed(NMParams params); - -/** - * Checks if the path passed as a parameter is valid and uses an internal path if it is null. - * - * @param context The thread context where the function is being executed. - * @param sourcePath Receives the path that Litebase will use to store and access tables. - * @param pathParam the path passed as a parameter. - * @return false if an error occurs; true, otherwise. - * @throws DriverException if the path passed as a parameter is invalid. - */ -bool checkApppath(Context context, TCHARP sourcePath, TCHARP pathParam); - -/** - * Verifies if the function can be applied to a data type field. - * - * @param parameterDataType The data type of the function parameter. - * @param sqlFunction The function code. - * @return true If the function can be applied to a data type field; false, otherwise. - */ -bool bindFunctionDataType(int32 parameterDataType, int32 sqlFunction); - -/** - * Returns the name of the data type function. - * - * @param sqlFunction The function code. - * @return A string with the function name. - */ -CharP dataTypeFunctionsName(int32 sqlFunction); - -/** - * Checks if the driver is opened and another parameter is not null when they are sent as parameters in some native methods. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The parameter to be checked. - * @param parameter The name of the parameter that can't be null. - * @throws IllegalStateException If the driver is closed. - * @throws NullPointerException If the table name is null. - */ -bool checkParamAndDriver(NMParams params, CharP parameter); - -/** - * Encrypts or decrypts all the tables of a connection given from the application id. - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - * @throws DriverException If a file error occurs or not all the tables use the desired cryptography format. - */ -void encDecTables(NMParams params, bool toEncrypt); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if LibClose() finished some structures. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_LibClose(TestSuite* testSuite, Context currentContext); - -/** - * Tests if LibOpen() initializes all the needed variables. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_LibOpen(TestSuite* testSuite, Context currentContext); - -/** - * Tests the function bindFunctionDataType() works properly. It is tested with all possible data types and data function types, including - * invalid values. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_bindFunctionDataType(TestSuite* testSuite, Context currentContext); - -/** - * Tests if the function checkApppath() accepts paths if and only if they are valid. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_checkApppath(TestSuite* testSuite, Context currentContext); - -/** - * Tests if the function dataTypeFunctionsName() returns the strings with the correct types. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_dataTypeFunctionsName(TestSuite* testSuite, Context currentContext); - -/** - * Tests if initVars() initializes all the needed variables. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_initVars(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/LitebaseGlobals.c b/LitebaseSDK/src/native/LitebaseGlobals.c deleted file mode 100644 index 59e493f1e7..0000000000 --- a/LitebaseSDK/src/native/LitebaseGlobals.c +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines all global variables used by Litebase. - */ - -#include "LitebaseGlobals.h" - -// Globas for driver creation. -Hashtable htCreatedDrivers = { 0 }; // The hash table for the created connections with Litebase. - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// The list of table files currently opened. -#if defined(POSIX) || defined(ANDROID) -XFilesList filesList; -#endif - -// Globals for the parser. -Hashtable reserved = { 0 }; // Table containing the reserved words. -MemoryUsageHT memoryUsage = { 0 }; // Indicates how much memory a select sql command uses in its temporary .db. -uint8 is[256] = { 0 }; // An array to help the selection of the kind of the token. -int8 function_x_datatype[10][7] = { // Matrix of data types which applies to the SQL functions. - {FUNCTION_DT_UPPER, FUNCTION_DT_LOWER, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_ABS , FUNCTION_DT_NONE , FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_ABS , FUNCTION_DT_NONE , FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_ABS , FUNCTION_DT_NONE , FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_ABS , FUNCTION_DT_NONE , FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_ABS , FUNCTION_DT_NONE , FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_UPPER, FUNCTION_DT_LOWER, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_NONE , FUNCTION_DT_NONE , FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_YEAR , FUNCTION_DT_MONTH, FUNCTION_DT_DAY, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE, FUNCTION_DT_NONE }, - {FUNCTION_DT_YEAR , FUNCTION_DT_MONTH, FUNCTION_DT_DAY, FUNCTION_DT_HOUR, FUNCTION_DT_MINUTE, FUNCTION_DT_SECOND, FUNCTION_DT_MILLIS}}; - -// An array with the names of the SQL data functions. -CharP names[10] = {"year", "month", "day", "hour", "minute", "second", "millis", "abs", "upper", "lower"}; - -// Used to count bits in an index bitmap. -uint8 bitsInNibble[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; - -JChar questionMark[2] = {(JChar)'?', (JChar)'\0'}; // A jchar string representing "?". - -// juliana@253_9: improved Litebase parser. - -// Classes used. -TCClass litebaseConnectionClass = { 0 }; // LitebaseConnection -TCClass loggerClass = { 0 }; // Logger - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Mutexes used. -DECLARE_MUTEX(parser); // Mutex for the parser. -DECLARE_MUTEX(log); // Mutex for logging. -DECLARE_MUTEX(files); // Mutex for the Litebase files list. - -// rnovais@568_10 @570_1 juliana@226_5 -// Aggregate functions table. -int8 aggregateFunctionsTypes[FUNCTION_AGG_SUM + 1] = {INT_TYPE, UNDEFINED_TYPE, UNDEFINED_TYPE, DOUBLE_TYPE, DOUBLE_TYPE}; - -// Data Type functions table. -int8 dataTypeFunctionsTypes[FUNCTION_DT_LOWER + 1] = {SHORT_TYPE, SHORT_TYPE, SHORT_TYPE, SHORT_TYPE, SHORT_TYPE, SHORT_TYPE, SHORT_TYPE, - UNDEFINED_TYPE, CHARS_TYPE, CHARS_TYPE}; -// Number of days in a month. -uint8 monthDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -// Each type size in the .db file. -uint8 typeSizes[11] = {4, 2, 4, 8, 4, 8, 4, -1, 4, 8, 4}; // rnovais@567_2: added more sizes. - -CharP errorMsgs_en[TOTAL_ERRORS] = { 0 }; // English error messages. -CharP errorMsgs_pt[TOTAL_ERRORS] = { 0 }; // Portuguese error messages. - -// juliana@220_4: added a crc32 code for every record. Please update your tables. -int32 crcTable[CRC32_SIZE] = { 0 }; // The crc32 table used to calculate a crc32 for a record. - -// TotalCross functions used by Litebase. -CharP2JCharPFunc TC_CharP2JCharP = { 0 }; -CharP2JCharPBufFunc TC_CharP2JCharPBuf = { 0 }; -CharP2TCHARPBufFunc TC_CharP2TCHARPBuf = { 0 }; -CharPToLowerFunc TC_CharPToLower = { 0 }; -JCharP2CharPFunc TC_JCharP2CharP = { 0 }; -JCharP2CharPBufFunc TC_JCharP2CharPBuf = { 0 }; -JCharP2TCHARPBufFunc TC_JCharP2TCHARPBuf = { 0 }; -JCharPEqualsJCharPFunc TC_JCharPEqualsJCharP = { 0 }; -JCharPEqualsIgnoreCaseJCharPFunc TC_JCharPEqualsIgnoreCaseJCharP = { 0 }; -JCharPHashCodeFunc TC_JCharPHashCode = { 0 }; -JCharPIndexOfJCharFunc TC_JCharPIndexOfJChar = { 0 }; -JCharPLenFunc TC_JCharPLen = { 0 }; -JCharToLowerFunc TC_JCharToLower = { 0 }; -JCharToUpperFunc TC_JCharToUpper = { 0 }; -TCHARP2CharPBufFunc TC_TCHARP2CharPBuf = { 0 }; -alertFunc TC_alert = { 0 }; -appendCharPFunc TC_appendCharP = { 0 }; // juliana@230_30 -appendJCharPFunc TC_appendJCharP = { 0 }; // juliana@230_30 -areClassesCompatibleFunc TC_areClassesCompatible = { 0 }; -createArrayObjectFunc TC_createArrayObject = { 0 }; -createObjectFunc TC_createObject = { 0 }; -createStringObjectFromCharPFunc TC_createStringObjectFromCharP = { 0 }; -createStringObjectFromTCHARPFunc TC_createStringObjectFromTCHARP = { 0 }; -createStringObjectWithLenFunc TC_createStringObjectWithLen = { 0 }; -debugFunc TC_debug = { 0 }; -double2strFunc TC_double2str = { 0 }; -executeMethodFunc TC_executeMethod = { 0 }; -getApplicationIdFunc TC_getApplicationId = { 0 }; -getAppPathFunc TC_getAppPath = { 0 }; -getDataPathFunc TC_getDataPath = { 0 }; -getDateTimeFunc TC_getDateTime = { 0 }; -getErrorMessageFunc TC_getErrorMessage = { 0 }; -getProcAddressFunc TC_getProcAddress = { 0 }; -getSettingsPtrFunc TC_getSettingsPtr = { 0 }; -getTimeStampFunc TC_getTimeStamp = { 0 }; -hashCodeFunc TC_hashCode = { 0 }; -hashCodeFmtFunc TC_hashCodeFmt = { 0 }; -heapAllocFunc TC_heapAlloc = { 0 }; -heapDestroyPrivateFunc TC_heapDestroyPrivate = { 0 }; -hstrdupFunc TC_hstrdup = { 0 }; -htFreeFunc TC_htFree = { 0 }; -htFreeContextFunc TC_htFreeContext = { 0 }; -htGet32Func TC_htGet32 = { 0 }; -htGet32InvFunc TC_htGet32Inv = { 0 }; -htGetPtrFunc TC_htGetPtr = { 0 }; -htNewFunc TC_htNew = { 0 }; -htPut32Func TC_htPut32 = { 0 }; -htPut32IfNewFunc TC_htPut32IfNew = { 0 }; -htPutPtrFunc TC_htPutPtr = { 0 }; -htRemoveFunc TC_htRemove = { 0 }; -int2CRIDFunc TC_int2CRID = { 0 }; -int2strFunc TC_int2str = { 0 }; -listFilesFunc TC_listFiles = { 0 }; -loadClassFunc TC_loadClass = { 0 }; -long2strFunc TC_long2str = { 0 }; -privateHeapCreateFunc TC_privateHeapCreate = { 0 }; -privateHeapSetJumpFunc TC_privateHeapSetJump = { 0 }; -privateXfreeFunc TC_privateXfree = { 0 }; -privateXmallocFunc TC_privateXmalloc = { 0 }; -privateXreallocFunc TC_privateXrealloc = { 0 }; -setObjectLockFunc TC_setObjectLock = { 0 }; -str2doubleFunc TC_str2double = { 0 }; -str2intFunc TC_str2int = { 0 }; -str2longFunc TC_str2long = { 0 }; -throwExceptionNamedFunc TC_throwExceptionNamed = { 0 }; -throwNullArgumentExceptionFunc TC_throwNullArgumentException = { 0 }; -tiF_create_siiFunc TC_tiF_create_sii = { 0 }; -toLowerFunc TC_toLower = { 0 }; -traceFunc TC_trace = { 0 }; -validatePathFunc TC_validatePath = { 0 }; // juliana@214_1 - -#ifdef ENABLE_MEMORY_TEST -getCountToReturnNullFunc TC_getCountToReturnNull = { 0 }; -setCountToReturnNullFunc TC_setCountToReturnNull = { 0 }; -#endif diff --git a/LitebaseSDK/src/native/LitebaseGlobals.h b/LitebaseSDK/src/native/LitebaseGlobals.h deleted file mode 100644 index b0787a3ce1..0000000000 --- a/LitebaseSDK/src/native/LitebaseGlobals.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares all global variables used by Litebase. - */ - -#ifndef LITEBASE_GLOBALS_H -#define LITEBASE_GLOBALS_H - -#include "Litebase.h" -#include "LitebaseTypes.h" - -// Globas for driver creation. -extern Hashtable htCreatedDrivers; // The hash table for the created connections with Litebase. - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// The list of table files currently opened. -#if defined(POSIX) || defined(ANDROID) -extern XFilesList filesList; -#endif - -// Globals for the parser. -extern Hashtable reserved; // Table containing the reserved words. -extern MemoryUsageHT memoryUsage; // Indicates how much memory a select sql command uses in its temporary .db. -extern uint8 is[256]; // An array to help the selection of the kind of the token. -extern int8 function_x_datatype[10][7]; // Matrix of data types which applies to the SQL functions. -extern CharP names[10]; // An array with the names of the SQL data functions. -extern uint8 bitsInNibble[16]; // Used to count bits in an index bitmap. -extern JChar questionMark[2]; // A jchar string representing "?". - -// juliana@parser_1: improved Litebase parser. - -// Classes used. -extern TCClass litebaseConnectionClass; // LitebaseConnection -extern TCClass loggerClass; // Logger - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Mutexes used. -extern DECLARE_MUTEX(parser); // Mutex for the parser. -extern DECLARE_MUTEX(log); // Mutex for logging. -extern DECLARE_MUTEX(files); // Mutex for the Litebase files list. - -// rnovais@568_10 @570_1 -extern int8 aggregateFunctionsTypes[FUNCTION_AGG_SUM + 1]; // Aggregate functions table. -extern int8 dataTypeFunctionsTypes[FUNCTION_DT_LOWER + 1]; // Data Type functions table. -extern uint8 monthDays[12]; // Number of days in a month. -extern uint8 typeSizes[11]; // Each type size in the .db file. - -// Error messages. -extern CharP errorMsgs_en[TOTAL_ERRORS]; // English error messages. -extern CharP errorMsgs_pt[TOTAL_ERRORS]; // Portuguese error messages. - -extern int32 crcTable[CRC32_SIZE]; // The crc32 table used to calculate a crc32 for a record. - -// TotalCross functions used by Litebase. -extern CharP2JCharPFunc TC_CharP2JCharP; -extern CharP2JCharPBufFunc TC_CharP2JCharPBuf; -extern CharP2TCHARPBufFunc TC_CharP2TCHARPBuf; -extern CharPToLowerFunc TC_CharPToLower; -extern JCharP2CharPBufFunc TC_JCharP2CharPBuf; -extern JCharP2CharPFunc TC_JCharP2CharP; -extern JCharP2TCHARPBufFunc TC_JCharP2TCHARPBuf; -extern JCharPEqualsJCharPFunc TC_JCharPEqualsJCharP; -extern JCharPEqualsIgnoreCaseJCharPFunc TC_JCharPEqualsIgnoreCaseJCharP; -extern JCharPHashCodeFunc TC_JCharPHashCode; -extern JCharPIndexOfJCharFunc TC_JCharPIndexOfJChar; -extern JCharPLenFunc TC_JCharPLen; -extern JCharToLowerFunc TC_JCharToLower; -extern JCharToUpperFunc TC_JCharToUpper; -extern TCHARP2CharPBufFunc TC_TCHARP2CharPBuf; -extern alertFunc TC_alert; -extern appendCharPFunc TC_appendCharP; // juliana@230_30 -extern appendJCharPFunc TC_appendJCharP; // juliana@230_30 -extern areClassesCompatibleFunc TC_areClassesCompatible; -extern createArrayObjectFunc TC_createArrayObject; -extern createObjectFunc TC_createObject; -extern createStringObjectFromCharPFunc TC_createStringObjectFromCharP; -extern createStringObjectFromTCHARPFunc TC_createStringObjectFromTCHARP; -extern createStringObjectWithLenFunc TC_createStringObjectWithLen; -extern debugFunc TC_debug; -extern double2strFunc TC_double2str; -extern executeMethodFunc TC_executeMethod; -extern getApplicationIdFunc TC_getApplicationId; -extern getAppPathFunc TC_getAppPath; -extern getDataPathFunc TC_getDataPath; -extern getDateTimeFunc TC_getDateTime; -extern getErrorMessageFunc TC_getErrorMessage; -extern getProcAddressFunc TC_getProcAddress; -extern getSettingsPtrFunc TC_getSettingsPtr; -extern getTimeStampFunc TC_getTimeStamp; -extern hashCodeFmtFunc TC_hashCodeFmt; -extern hashCodeFunc TC_hashCode; -extern heapAllocFunc TC_heapAlloc; -extern heapDestroyPrivateFunc TC_heapDestroyPrivate; -extern hstrdupFunc TC_hstrdup; -extern htFreeFunc TC_htFree; -extern htFreeContextFunc TC_htFreeContext; -extern htGet32Func TC_htGet32; -extern htGet32InvFunc TC_htGet32Inv; -extern htGetPtrFunc TC_htGetPtr; -extern htNewFunc TC_htNew; -extern htPut32Func TC_htPut32; -extern htPut32IfNewFunc TC_htPut32IfNew; -extern htPutPtrFunc TC_htPutPtr; -extern htRemoveFunc TC_htRemove; -extern int2CRIDFunc TC_int2CRID; -extern int2strFunc TC_int2str; -extern listFilesFunc TC_listFiles; -extern loadClassFunc TC_loadClass; -extern long2strFunc TC_long2str; -extern privateHeapCreateFunc TC_privateHeapCreate; -extern privateHeapSetJumpFunc TC_privateHeapSetJump; -extern privateXfreeFunc TC_privateXfree; -extern privateXmallocFunc TC_privateXmalloc; -extern privateXreallocFunc TC_privateXrealloc; -extern setObjectLockFunc TC_setObjectLock; -extern str2doubleFunc TC_str2double; -extern str2intFunc TC_str2int; -extern str2longFunc TC_str2long; -extern throwExceptionNamedFunc TC_throwExceptionNamed; -extern throwNullArgumentExceptionFunc TC_throwNullArgumentException; -extern tiF_create_siiFunc TC_tiF_create_sii; -extern toLowerFunc TC_toLower; -extern traceFunc TC_trace; -extern validatePathFunc TC_validatePath; // juliana@214_1 -#ifdef ENABLE_MEMORY_TEST -extern getCountToReturnNullFunc TC_getCountToReturnNull; -extern setCountToReturnNullFunc TC_setCountToReturnNull; -#endif - -#endif diff --git a/LitebaseSDK/src/native/LitebaseTypes.h b/LitebaseSDK/src/native/LitebaseTypes.h deleted file mode 100644 index 1f21eb4b9d..0000000000 --- a/LitebaseSDK/src/native/LitebaseTypes.h +++ /dev/null @@ -1,1835 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares all the types used by Litebase. - */ - -#ifndef LITEBASE_TYPES_H -#define LITEBASE_TYPES_H - -#include "tcvm.h" -#include "Constants.h" -#include "Macros.h" - -#if defined(linux) -#include -#endif - -// Buffers for string buffers for converting to string of known types. -/** - * Buffer for a date type of the form YYYY/MM/DD. - */ -typedef char DateBuf[11]; - -/** - * Buffer for a datetime type of the form YYYY/MM/DD HH:MM:SS:MMM XM. - */ -typedef char DateTimeBuf[27]; - -/** - * Function definition to list PDBs. - */ -typedef void (*tiPDBF_listPDBs_iiFunc)(NMParams p); - -/** - * A list of objects used to hold prepared statements that uses a specific table. - */ -TC_DeclareList(TCObject); - -// Typedefs for using Litebase file. -typedef struct XFile XFile; -typedef struct Key Key; -typedef void (*setPosFunc)(XFile* xFile, int32 position); -typedef bool (*growToFunc)(Context context, XFile* xFile, uint32 newSize); -typedef bool (*readBytesFunc)(Context context, XFile* xFile, uint8* buffer, int32 count); -typedef bool (*writeBytesFunc)(Context context, XFile* xFile, uint8* buffer, int32 count); -typedef bool (*closeFunc)(Context context, XFile* xFile); - -// Typedefs for the structures used by Litebase. -// juliana@parser_1: improved Litebase parser. -typedef struct SQLValue SQLValue; -typedef struct SQLSelectClause SQLSelectClause; -typedef struct SQLColumnListClause SQLColumnListClause; -typedef struct LitebaseParser LitebaseParser; -typedef struct SQLBooleanClause SQLBooleanClause; -typedef struct SQLBooleanClauseTree SQLBooleanClauseTree; -typedef struct SQLDeleteStatement SQLDeleteStatement; -typedef struct SQLFieldDefinition SQLFieldDefinition; -typedef struct SQLInsertStatement SQLInsertStatement; -typedef struct SQLResultSetField SQLResultSetField; -typedef struct SQLResultSetTable SQLResultSetTable; -typedef struct SQLSelectStatement SQLSelectStatement; -typedef struct SQLUpdateStatement SQLUpdateStatement; -typedef struct PlainDB PlainDB; -typedef struct Table Table; -typedef struct IntVector IntVector; -typedef struct ShortVector ShortVector; -typedef struct ResultSet ResultSet; -typedef struct Node Node; -typedef struct MarkBits MarkBits; -typedef struct Index Index; -typedef struct ComposedIndex ComposedIndex; -typedef struct FirstLast FirstLast; -typedef struct MemoryUsageEntry MemoryUsageEntry; -typedef struct MemoryUsageHT MemoryUsageHT; -typedef struct StringArray StringArray; // juliana@227_20 - -/** - * A generic file structure, which can be used for normal and memory files. - */ -struct XFile -{ - /** - * Normal file: codewarrior does not like annonimous unions. - */ - NATIVE_FILE file; - - /** - * A cache for the file so that the bytes do not needed to be loaded all the time. - */ - uint8* cache; - - /** - * Memory file buffer. - */ - uint8* fbuf; - - /** - * The current cache position. - */ - int32 cachePos; - - /** - * The initial position of the file into the cache. - */ - int32 cacheIni; - - /** - * The final position of the file into the cache. - */ - int32 cacheEnd; - - /** - * The cache size. - */ - int32 cacheInitialSize; - - /** - * Indicates the first position of the cache that is dirty. - */ - int32 cacheDirtyIni; - - /** - * Indicates the last position of the cache that is dirty. - */ - int32 cacheDirtyEnd; - - /** - * The file size. - */ - uint32 size; - - /** - * The current file position. - */ - int32 position; - - /** - * The last position of the file used. - */ - int32 finalPos; - - /** - * The file name, which is empty for a memory file. - */ - char name[DBNAME_SIZE]; - - /** - * Indicates if the cache is dirty and its contents needs to be saved later on. - */ - uint8 cacheIsDirty; - - // juliana@227_3: improved table files flush dealing. - /** - * Indicates if the cache file should not be flushed. - */ - uint8 dontFlush; - - /** - * Indicates if the table uses cryptography. - */ - uint8 useCrypto; // juliana@crypto_1: now Litebase supports weak cryptography. - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -#if defined(POSIX) || defined(ANDROID) - /** - * The file full path; - */ - char fullPath[MAX_PATHNAME + 1]; - - /** - * The timestamp of the last time the file was used. - */ - int32 timeStamp; -#endif -}; - -#if defined(POSIX) || defined(ANDROID) -typedef struct XFilesList XFilesList; - -/** - * Pointer to a list of currently opened Litebase files. - */ -struct XFilesList -{ - /** - * An array with the used to store the list of Litebase files. - */ - XFile* list[MAX_OPEN_FILES]; - - /** - * The number of positions used. - */ - int32 count; -}; -#endif - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * This structure represents the key of a record. It may be any of the SQL types defined here. - */ -struct Key -{ - /** - * The record index or NO_VALUE. - */ - int32 record; - - /** - * The values stored in the key. - */ - SQLValue* keys; - - /** - * The index that has this key. - */ - Index* index; -}; - -/** - * Represents a value which can be inserted in a column of a table. - */ -struct SQLValue -{ - /** - * Represents the CHARS, VARCHAR, CHARS NOCASE, and VARCHAR NOCASE data types. - */ - JCharP asChars; - - /** - * Represents the BLOB data type. - */ - uint8* asBlob; // juliana@210_5: removed memory leaks concerning blobs. - - struct - { - uint32 length:31; - uint32 isNull: 1; - }; - union - { - /** - * Represents the SHORT data type. - */ - int16 asShort; - - /** - * Represents the INT data type. - */ - int32 asInt; - - /** - * Represents the LONG data type. - */ - int64 asLong; - - /** - * Represents the FLOAT data type. - */ - float asFloat; - - /** - * Represents the DOUBLE data type. - */ - double asDouble; - - struct // type DATETIME. - { - /** - * Represents the date part. - */ - int32 asDate; - - /** - * Represents the time part. - */ - int32 asTime; - }; - }; -}; - -struct SQLSelectClause -{ - /** - * Number of fields found. - */ - uint8 fieldsCount; - - /** - * Indicates if the select clause has data type functions. - */ - uint8 hasDTFunctions; // rnovais@568_10 - - /** - * Indicates if the select clause has aggregated functions. - */ - uint8 hasAggFunctions; - - /** - * Indicates if the select clause has real columns. - */ - uint8 hasRealColumns; - - /** - * Indicates if the select clause field list was built from a wildcard. - */ - uint8 hasWildcard; - - /** - * Indicates the type of the select clause. - * This will be better used when we implement a PLANNER for litebase. Up to now this only indicates if the select clause has a count(*). - */ - uint8 type; - - /** - * Indicates if the select clause comes from a prepared select statement. - */ - uint8 isPrepared; - - /** - * The number of tables in the table list. - */ - uint8 tableListSize; - - /** - * The select sql query hash code for the memory usage hash table. - */ - int32 sqlHashCode; - - /** - * The resulting ResultSet table list. - */ - SQLResultSetTable** tableList; - - /** - * The resulting ResultSet field list. - */ - SQLResultSetField** fieldList; - - /** - * The index of the fields. - */ - Hashtable htName2index; - - /** - * The heap used by the parser is stored here to allocate the structures for the select statement. - */ - Heap heap; -}; - -/** - * This structure represents a SQL column list clause, like order by or group by.
    - * Note: The select clause has a different structure SQLSelectClause, since it has a different complexity. - */ -struct SQLColumnListClause -{ - /** - * Indicates that the index to be used is composed or not. - */ - uint8 isComposed; // juliana@230_29: order by and group by now use indices on simple queries. - - /** - * Indicates the index to use when doing a sort operation. - */ - int16 index; // juliana@230_29: order by and group by now use indices on simple queries. - - /** - * Number of fields. - */ - int32 fieldsCount; - - /** - * The column field list. - */ - SQLResultSetField** fieldList; - - /** - * Backup for the tableColIndexes, used in prepared statements. - */ - uint8* fieldTableColIndexesBak; // guich@554_37 -}; - -/* The type which returns the result of the parser */ -struct LitebaseParser -{ - /** - * The type of SQL command, which can be one of: CMD_CREATE_TABLE, CMD_CREATE_INDEX, - * CMD_DROP_TABLE, CMD_DROP_INDEX, CMD_ALTER_DROP_PK, - * CMD_ALTER_ADD_PK, CMD_ALTER_RENAME_TABLE, CMD_ALTER_RENAME_COLUMN, - * CMD_SELECT, CMD_INSERT, CMD_UPDATE, or CMD_DELETE. - */ - uint8 command; - - /** - * Counts the number of simple primary keys, which must be only one. - */ - uint8 numberPK; - - /** - * The number of fields in the field list. - */ - uint8 fieldListSize; - - /** - * The number of fields of values in the field values list. - */ - uint8 fieldValuesSize; - - /** - * The number of fields of the update field list. - */ - uint8 fieldNamesSize; - - /** - * This is used to differ between a where clause and a having clause. Before parsing the having clause, isWhereClause is set to - * false. So the getInstanceBooleanClause() method will return a having clause, otherwise it returns a where clause. - */ - uint8 isWhereClause; - - /** - * The last position of the buffer read. - */ - uint16 yyposition; - - /** - * The length of the sql string to be parsed. - */ - uint16 length; - - /** - * The name of a token. - */ - VoidP yylval; // juliana@parser_1: improved Litebase parser. - - // juliana@parser_1: improved Litebase parser. - /** - * The first table name found in an update statement. - */ - CharP firstFieldUpdateTableName; - - /** - * The first table alias found in an update statement. - */ - CharP firstFieldUpdateAlias; - - /** - * The second table name found in an update statement, which indicates an error. - */ - CharP secondFieldUpdateTableName; - - /** - * The second table alias found in an update statement, which indicates an error. - */ - CharP secondFieldUpdateAlias; - - /** - * The last char read. - */ - JChar yycurrent; - - /** - * An auxiliary expression tree. - */ - SQLBooleanClauseTree* auxTree; - - /** - * An auxiliary field. - */ - SQLResultSetField* auxField; - - /** - * Contains field values (strings) used on insert/update statements. - */ - JCharP fieldValues[MAXIMUMS]; - - /** - * The field list for inserts, updates and indices. - */ - CharP fieldNames[MAXIMUMS]; - - /** - * The resulting set table list, used with all statements. - */ - SQLResultSetTable* tableList[MAXIMUMS]; - - /** - * The field list for the SQL commands except SELECT. - */ - SQLFieldDefinition* fieldList[MAXIMUMS]; - - /** - * The field list for selects. - */ - SQLResultSetField* selectFieldList[MAXIMUMS]; - - /** - * The where clause of a SELECT statement. - */ - SQLBooleanClause* whereClause; - - /** - * The having clause of a SELECT statement. - */ - SQLBooleanClause* havingClause; - - /** - * The initial part of the SELECT statement - */ - SQLSelectClause select; - - /** - * The order by part of a SELECT statement. - */ - SQLColumnListClause orderBy; - - /** - * The group by part of a SELECT statement. - */ - SQLColumnListClause groupBy; - - /** - * A pre-allocated field list for order by. - */ - SQLResultSetField* orderByfieldList[MAXIMUMS]; - - /** - * A pre-allocated field list for group by. - */ - SQLResultSetField* groupByfieldList[MAXIMUMS]; - - /** - * A list of all fields referenced in the where boolean clause. - */ - SQLResultSetField* whereFieldList[MAXIMUMS]; - - /** - * The list of trees that contains the paramameter list of the where boolean clause. - */ - SQLBooleanClauseTree* whereParamList[MAXIMUMS]; - - /** - * A list of all fields referenced in the having boolean clause. - */ - SQLResultSetField* havingFieldList[MAXIMUMS]; - - /** - * The list of trees that contains the paramameter list of the having boolean clause. - */ - SQLBooleanClauseTree* havingParamList[MAXIMUMS]; - - /** - * A hashtable to be used on select statements to verify if it has repeated table names. - */ - Hashtable tables; - - /** - * The input device. - */ - JCharP zzReaderChars; - - /** - * The heap to allocate the parser structure. - */ - Heap heap; - - /** - * The thread context in order to throw exceptions. - */ - Context context; -}; - -struct SQLBooleanClause -{ - /** - * The number of fields. - */ - uint8 fieldsCount; - - /** - * The length of the parameter list. - */ - uint8 paramCount; - - /** - * The number of indexes to be applied. - */ - uint8 appliedIndexesCount; - - /** - * The boolean operator to be used to combine the result set of each index. Can be either SQLElement.OP_BOOLEAN_AND, - * SQLElement.OP_BOOLEAN_OR, or SQLElement.OP_BOOLEAN_NONE (in case only one index was used). - */ - uint8 appliedIndexesBooleanOp; - - /** - * Indicates if it is a where clause or a having clause. - */ - uint8 isWhereClause; - - /** - * Indicates if the result set will be indexed. - */ - int8 appliedIndexRs; - - /** - * Type of the where clause: AND of different result sets = 0, and OR of different result sets = 1. - */ - uint8 type; - - /** - * Resulting boolean clause expression tree. - */ - SQLBooleanClauseTree* expressionTree; - - /** - * The original boolean clause expression tree. This is necessary when freeing the tree. - */ - SQLBooleanClauseTree* origExpressionTree; - - /** - * The associated result set. - */ - ResultSet* resultSet; - - /** - * Table that maps the field name to an index in the field list. - */ - Hashtable fieldName2Index; - - /** - * A backup of the expression tree. - */ - SQLBooleanClauseTree* expressionTreeBak; - - /** - * A list of all fields referenced in the boolean clause. - */ - SQLResultSetField** fieldList; - - /** - * The list of trees that contains the parameter list of the boolean clause. - */ - SQLBooleanClauseTree** paramList; - - /** - * The constant values to be used by the indexes that were applied to the boolean clause. - */ - SQLBooleanClauseTree* appliedIndexesValueTree[MAX_NUM_INDEXES_APPLIED]; - - /** - * The tables of the correspondent indexes. - */ - Table* appliedIndexesTables[MAX_NUM_INDEXES_APPLIED]; - - /** - * The composed indices applied. - */ - ComposedIndex* appliedComposedIndexes[MAX_NUM_INDEXES_APPLIED]; - - /** - * The columns whose indexes were applied to the boolean clause. A column can be listed more than once, in case it is listed more than once in the - * boolean clause. - */ - uint8 appliedIndexesCols[MAX_NUM_INDEXES_APPLIED]; - - /** - * The relational operators to be used by the indexes that were applied to the boolean clause. - */ - uint8 appliedIndexesRelOps[MAX_NUM_INDEXES_APPLIED]; -}; - -struct SQLBooleanClauseTree -{ - /** - * Tree operand type. - */ - uint8 operandType; - - /** - * Indicate if the value type is a floating point type. - */ - uint8 isFloatingPointType; - - /** - * The associated table column index of the operand. - */ - uint8 colIndex; - - /** - * Indicates if this is a parameter. - */ - uint8 isParameter; - - /** - * Indicates if the parameter value is defined. - */ - uint8 isParamValueDefined; - - /** - * Pattern matching type. - */ - uint8 patternMatchType; - - /** - * Position of the % in the string. - */ - uint8 posPercent; // rnovais@568_1 - - /** - * Indicates if the left and right tree are identifiers. - */ - uint8 bothAreIdentifier; - - /** - * Indicates if it has an associated index. Used on join table1.field1 = table2.field2. - */ - uint8 hasIndex; - - /** - * The length of strToMatch - */ - uint8 lenToMatch; - - /** - * The index of the correspondent result set. - */ - int8 indexRs; - - /** - * The value data type. - */ - int8 valueType; - - /** - * The operand name hash code. - */ - int32 nameHashCode; - - /** - * The operand name hash code used only for sql functions. - */ - int32 nameSqlFunctionHashCode; // rnovais@570_108 - - // Subtrees - /** - * The left tree. - */ - SQLBooleanClauseTree* leftTree; - - /** - * The right tree. - */ - SQLBooleanClauseTree* rightTree; - - /** - * The parent tree. - */ - SQLBooleanClauseTree* parent; - - /** - * The associated SQLBooleanClause. - */ - SQLBooleanClause* booleanClause; - - /** - * Tree operand name. - */ - CharP operandName; - - /** - * String to do the pattern match. - */ - JCharP strToMatch; - - /** - * Tree operand value. - */ - SQLValue operandValue; - - /** - * The current value. Used only on joins comparing table fields like table1.field1 table2.field2. - */ - SQLValue valueJoin; -}; - -/** - * Represents a SQL DELETE statement. - */ -struct SQLDeleteStatement -{ - /** - * The statement type, which indicates that this is a DELETE statement. - */ - uint8 type; - - /** - * The where clause of the delete statement. - */ - SQLBooleanClause* whereClause; - - /** - * The structure with the table of the delete statement. - */ - SQLResultSetTable* rsTable; - - /** - * The heap to allocate memory for the delete statement. - */ - Heap heap; -}; - -/** - * Represents a field of a statement except for SELECTs. - */ -struct SQLFieldDefinition -{ - /** - * The name of the field. - */ - CharP fieldName; - - /** - * The default value of a field can contain a string, a number or a Date/Datetime value. This must be converted to the correct type later. If - * defaultValue is null, the default value was not defined. - */ - JCharP defaultValue; - - /** - * Only used for chars / chars no case / blob types. For other types it is equal to zero. - */ - int32 fieldSize; - - /** - * The type of the field. It can be: NUMBER, UNDEFINED, CHARS, - * SHORT, INT, LONG, FLOAT, DOUBLE, - * CHARS_NOCASE, BOOLEAN, DATE, or DATE_TIME. - */ - uint8 fieldType; - - /** - * Defines if the field can be null or not. - */ - uint8 isNotNull; - - /** - * Indicates if the field is the primary key. - */ - uint8 isPrimaryKey; -}; - -/** - * Represents a SQL INSERT statement. - */ -struct SQLInsertStatement -{ - /** - * The statement type, which indicates that this is a INSERT statement. - */ - uint8 type; - - /** - * The number of values to be inserted. - */ - uint8 nFields; - - /** - * The number of the parameters if the insert statement is a preprared statement. - */ - uint8 paramCount; - - /** - * The array with the indexes of the parameters. - */ - uint8* paramIndexes; - - /** - * An array that indicates if a parameters is defined or not. - */ - uint8* paramDefined; - - /** - * An array that indicates if a null value will be stored in a field. - */ - uint8* storeNulls; - - /** - * The table name. - */ - CharP tableName; - - /** - * The base table used by the SQL expression. - */ - Table* table; - - /** - * The fields used if the insert statement is not using the default order. - */ - CharP* fields; - - /** - * The record to be inserted. - */ - SQLValue** record; - - /** - * The heap to allocate memory for the insert statement. - */ - Heap heap; -}; - -/** - * Represents a field of a ResultSet. - */ -struct SQLResultSetField -{ - /** - * Indicates what resultset it belongs. - */ - uint8 indexRs; - - /** - * Indicates if this is a wildcard field. - */ - uint8 isWildcard; - - /** - * Indicates if the field represents a virtual column (not mapped directly to the underlying table). - */ - uint8 isVirtual; - - /** - * Indicates if the function is an aggregated function. - */ - uint8 isAggregatedFunction; - - /** - * Indicates if the result is to be shown in ascending or decreasing order for fields from order by clause. - */ - uint8 isAscending; - - /** - * Indicates if the function is a data type function. - */ - uint8 isDataTypeFunction; - - /** - * The index of the column that this field represents in the underlying table. For virtual fields, this value equals -1. - */ - uint8 tableColIndex; // juliana@227_1: solved a problem with selecting all the columns of a 128-column table. - - /** - * Indicates that the index to be used is composed or not. - */ - uint8 isComposed; // juliana@230_21: MAX() and MIN() now use indices on simple queries. - - /** - * The sql function that this field represents. - */ - int8 sqlFunction; - - /** - * The data type. - */ - int8 dataType; - - /** - * Indicates the index to use when doing a max() or min() operation. - */ - int8 index; // juliana@230_21: MAX() and MIN() now use indices on simple queries. - - /** - * The column name hash code. - */ - int32 tableColHashCode; - - /** - * The field alias hash code. - */ - int32 aliasHashCode; - - /** - * The size of the field; only used in chars types and blob. - */ - int32 size; - - /** - * Indicates the table name it belongs. The parser sets its value. e. g.
    - * select person.age from test: tableName = person. - */ - CharP tableName; - - /** - * The field alias. - */ - CharP alias; - - /** - * The name of the column that this field represents in the underlying table. For virtual fields, this value equals null. - */ - CharP tableColName; - - /** - * The parameter of the function.
    - * Note: It is declared as ResultSetField to allow nested function calls in the future. - */ - SQLResultSetField* parameter; - - /** - * Indicates what table it belongs. - */ - Table* table; -}; - -/** - * Represents a table of various statements, except for inserts. - */ -struct SQLResultSetTable -{ - /** - * The object table, filled when binding the statement. - */ - Table* table; - - /** - * The name of the table, filled during the parsing process. - */ - CharP tableName; - - /** - * The Table alias. - */ - CharP aliasTableName; - - /** - * The alias table name hash code. - */ - int32 aliasTableNameHashCode; -}; - -/** - * Represents a SQL SELECT statement. - */ -struct SQLSelectStatement -{ - /** - * The statement type, which indicates that this is a SELECT statement. - */ - uint8 type; - - /** - * The select clause of the statement. - */ - SQLSelectClause* selectClause; - - /** - * The group by clause of the statement. - */ - SQLColumnListClause* groupByClause; - - /** - * The order by clause of the statement. - */ - SQLColumnListClause* orderByClause; - - /** - * The where clause of the statement. - */ - SQLBooleanClause* whereClause; - - /** - * The having clause of the statement. - */ - SQLBooleanClause* havingClause; -}; - -/** - * Represents a SQL UPDATE statement. - */ -struct SQLUpdateStatement -{ - /** - * The statement type, which indicates that this is an UPDATE statement. - */ - uint8 type; - - /** - * The number of values to be updated. - */ - uint8 nValues; - - /** - * The number of the parameters if the update statement is a preprared statement. - */ - uint8 paramCount; - - /** - * The array with the indexes of the parameters. - */ - uint8* paramIndexes; - - /** - * An array that indicates if a parameters is defined or not. - */ - uint8* paramDefined; - - /** - * An array that indicates if a null value will be stored in a field. - */ - uint8* storeNulls; - - /** - * The base table used by the SQL expression. - */ - SQLResultSetTable* rsTable; - - /** - * The where clause. - */ - SQLBooleanClause* whereClause; - - /** - * The fields used to update a record. - */ - CharP* fields; - - /** - * The record to be inserted. - */ - SQLValue** record; - - /** - * The heap to allocate memory for the update statement. - */ - Heap heap; -}; - - /** - * Stores the table files and some variables concerning them. - */ -struct PlainDB -{ - /** - * Indicates if a table was not correctly closed when was oppened for the last time. - */ - uint8 wasNotSavedCorrectly; - - /** - * Indicates whether a table used the wrong cryptography format. - */ - uint8 useOldCrypto; - - /** - * Indicates if the tables of this connection use ascii or unicode strings. - */ - uint8 isAscii; // juliana@210_2: now Litebase supports tables with ascii strings. - - /** - * The size of a row. - */ - uint16 rowSize; - - /** - * The table header size. - */ - uint16 headerSize; - - /** - * The number of rows. - */ - int32 rowCount; - - /** - * The current row increment when inserting data on the table. - */ - int32 rowInc; - - /** - * The number of rows available. - */ - int32 rowAvail; // rnovais@112_2 - - /** - * A buffer to read a row. - */ - uint8* basbuf; - - /** - * The table name. It is empty when the table is temporary. - */ - char name[DBNAME_SIZE]; - - /** - * The database (.db) file. - */ - XFile db; - - /** - * The strings and blobs (.dbo) file. - */ - XFile dbo; - - /** - * The pointer to a function to set the position of the record in the files. - */ - setPosFunc setPos; - - /** - * The pointer to a function to grow the files. - */ - growToFunc growTo; - - /** - * The pointer to a function to read bytes from the file. - */ - readBytesFunc readBytes; - - /** - * The pointer to a function to write bytes to the file. - */ - writeBytesFunc writeBytes; - - /** - * The pointer to a function to close the file. - */ - closeFunc close; -}; - -/** - * A growable int array. - */ -struct IntVector -{ - /** - * The array itself. - */ - int32* items; - - /** - * Allocated length of the array. - */ - int16 length; - - /** - * Current number of items count. - */ - int16 size; - - /** - * A heap to store the array. - */ - Heap heap; -} ; - -/** - * The table structure. - */ -struct Table -{ - /** - * The number of columns of this table. - */ - uint8 columnCount; - - /** - * Number of composed primary key columns. - */ - uint8 numberComposedPKCols; - - /** - * Number of composed indices. - */ - uint8 numberComposedIndexes; - - /** - * Indicates that a table has been modified and must be marked as not closed properly after opened and before closed. - */ - uint8 isModified; // juliana@226_4 - - /** - * The table version. - */ - uint8 version; // juliana@230_12 - - /** - * Indicates if the table was updated after the last time it was opened. - */ - uint8 wasUpdated; // juliana@270_27: now purge will also really purge the table if it only suffers updates. - - /** - * The primary key column. - */ - int8 primaryKeyCol; // juliana@114_9 - - /** - * The index of the composed primary key. - */ - int8 composedPK; - - /** - * Used to order the tables. - */ - int16 weight; - - /** - * The counter of the current rowid. The rowid is continuously incremented so that two elements will never have the same - * one, even if elements are deleted.

    The record attributes are stored in the first two bits of the rowid. - */ - int32 currentRowId; - - /** - * The attributes of the row. - */ - int32 auxRowId; // rnovais@570_61 - - /** - * The number of deleted rows, which is always logical. - */ - int32 deletedRowsCount; - - /** - * Used to return the number of rows that a select without a where clause returned. - */ - int32 answerCount; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * The maximum length of the bit map representing all table rows. - */ - int32 allRowsBitmapLength; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * The column attributes. - */ - uint8* columnAttrs; - - /** - * Just for the case when the column has a default value but the user explicited the insert or update of a null. - */ - uint8* storeNulls; - - /** - * Contains the null values. - */ - uint8* columnNulls; - - /** - * The composed primary key columns. - */ - uint8* composedPrimaryKeyCols; - - /** - * A map with rows that satisfy totally the query WHERE clause. - */ - uint8* allRowsBitmap; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * Column offsets within the record. - */ - uint16* columnOffsets; - - /** - * Column types (SHORT, INT, LONG, FLOAT, DOUBLE, CHARS, - * CHARS_NOCASE) - */ - int8* columnTypes; - - /** - * The hashes of the column names. - */ - int32* columnHashes; - - /** - * Column sizes (only used for CHAR and BLOB types). - */ - int32* columnSizes; - - /** - * The full name of the table. - */ - char name[DBNAME_SIZE]; - - /** - * The path where the table is stored if it is stored in disk. - */ - TCHARP sourcePath; - - /** - * The column names. If null, the column names are not available because it is a temporary table. - */ - CharP* columnNames; - - /** - * Existing column indices for each column, or null if the column has no index. - */ - Index** columnIndexes; - - /** - * The corresponding files of the table. - */ - PlainDB db; - - /** - * Given a column name, returns its index for this table. rowid, a special column, is always column 0. - */ - Hashtable htName2index; - - /** - * Contains the default values for the columns. - */ - SQLValue** defaultValues; - - /** - * An array of nodes indices. - */ - int32* nodes; // juliana@noidr_2: the maximum number of keys of a index was duplicated. - - /** - * Existing composed column indices for each column, or null if the table has no composed index. - */ - ComposedIndex* composedIndexes[MAX_NUM_INDEXES_APPLIED]; - - /** - * A linked list of prepared statements that use this table. - */ - TCObjects* preparedStmts; - - /** - * A heap to allocate the table structure. - */ - Heap heap; -}; - -/** - * Represents a set or rows resulting from a LitebaseConnection.executeQuery() method call. - */ -struct ResultSet -{ - /** - * Indicates if it is a select of the form select * from table or not. - */ - uint8 isSimpleSelect; // juliana@210_1: select * from table_name does not create a temporary table anymore. - - /** - * Indicates if the select table is temporary or not. - */ - uint8 isTempTable; // juliana@223_14: solved possible memory problems. - - /** - * Counts the number of indices when running generateIndexedRowsMap(). - */ - uint8 indexCount; - - /** - * When rowsBitmap is generated, indicates what is the boolean relationship between the rows marked in the bitmap and any remaining - * WHERE clause. - */ - uint8 rowsBitmapBoolOp; - - // juliana@223_13: corrected a bug that could break the application when freeing a result set of a prepared statement. - /** - * Indicates that this ResultSet was generated by a select prepared statement. - */ - uint8 isPrepared; - - /** - * The index of the correspodent result set. - */ - int8 indexRs; - - /** - * The number of columns in this result set. - */ - uint16 columnCount; - - /** - * Current record position being read. - */ - int32 pos; - - /** - * The number of valid records of this result set. - */ - int32 answerCount; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * An array with the number of decimal places that is used to format float and double values, when being retrieved using - * the getString() method. This can be set at runtime by the user, and it is -1 as default. - */ - int8* decimalPlaces; - - /** - * A map with rows that satisfy totally the query WHERE clause. - */ - uint8* allRowsBitmap; // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - - /** - * The associated table for the result set. - */ - Table* table; - - /** - * A map with rows that satisfy totally or partially the query WHERE clause; generated using the table indices. - */ - IntVector rowsBitmap; - - /** - * The indices used in this result set. - */ - IntVector indexes; - - /** - * An auxiliary map with rows that satisfy totally or partially the query WHERE clause; generated from the table indices. - */ - IntVector auxRowsBitmap; - - /** - * Contains the hash of the all possible colunm names in the select statement. - */ - Hashtable intHashtable; - - /** - * The WHERE clause associated with the result set. - */ - SQLBooleanClause* whereClause; - - /** - * The select clause of the sql that generated this result set. - */ - SQLSelectClause* selectClause; - - /** - * Generates the result set indexed rows map from the associated table indexes applied to the associated WHERE clause. - */ - MarkBits* markBits; - - /** - * A heap to allocate the result set structure. - */ - Heap heap; - - /** - * The connection with Litebase. - */ - TCObject driver; -}; - -/** - * This is the implementation of a B-Tree. - */ -struct Node // for B-tree -{ - /** - * Indicates if a node is dirty. - */ - uint8 isDirty; - - /** - * The index of a node in the B-Tree. - */ - uint16 idx; - - /** - * The size of the node. - */ - uint16 size; - - // juliana@noidr_2: the maximum number of keys of a index was duplicated. - /** - * This children nodes. - */ - uint16* children; // Each array has one extra component, to allow for possible overflow. - - /** - * The index of this node. - */ - Index* index; - - /** - * The keys that this node stores. - */ - Key* keys; -}; - -/** - * Generates the result set indexed rows map from the associated table indexes applied to the associated WHERE clause. - */ -struct MarkBits -{ - /** - * Indicates if a value is equal or not. - */ - uint8 isNoLongerEqual; - - /** - * The value of a bit of the bitmap. - */ - uint8 bitValue; - - /** - * The left operator. - */ - uint8* leftOp; - - /** - * The right operator. - */ - uint8* rightOp; - - /** - * The index bitmap of the where clause. - */ - IntVector* indexBitmap; - - /** - * The left key. - */ - Key leftKey; - - /** - * The right key. - */ - Key rightKey; -} ; - -/** - * Represents a B-Tree header. - */ -struct Index // renamed from BTree to Index -{ - /** - * If the keys are mostly ordered (like the rowid), makes the nodes more full. - */ - uint8 isOrdered; // guich@110_5 - - /** - * Indicates if the write of the node is delayed. - */ - uint8 isWriteDelayed; - - /** - * The number of columns of the index: 1 means simple index. - */ - uint8 numberColumns; - - /** - * The maximun number of keys per node. - */ - uint8 btreeMaxNodes; - - /** - * A cache of node. - */ - uint8 cacheI; - - /** - * The size of the keys. - */ - uint8 keyRecSize; - - /** - * The current number of nodes in the nodes array. - */ - uint8 nodesArrayCount; - - /** - * The size of the nodes. - */ - uint16 nodeRecSize; - - /** - * The number of nodes. - */ - uint16 nodeCount; - - /** - * A buffer to be used to save and load data from the index. - */ - uint8 basbuf[SECTOR_SIZE]; - - /** - * The types of the columns of the index. - */ - int8* types; - - /** - * The sizes of the columns of the index. - */ - int32* colSizes; - - /** - * The name of the index table. - */ - char name[DBNAME_SIZE]; - - /** - * The nodes file. - */ - XFile fnodes; - - // juliana@noidr_1: removed .idr files from all indices and changed its format. - - /** - * The cache of the index. - */ - Node* cache[CACHE_SIZE]; - -// juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - /** - * The first level of the index B-tree. - */ - Node** firstLevel; - - /** - * The table of the index. - */ - Table* table; - - /** - * The root of the tree. - */ - Node* root; - - /** - * The heap to allocate the index structure. - */ - Heap heap; - - /** - * An array for climbing on index nodes. - */ - size_t nodes[4]; // juliana@230_32: corrected a bug of searches in big indices not returning all the results. -}; - -/** - * Represents a composed index. - */ -struct ComposedIndex -{ - /** - * Identifies the composed index. - */ - uint8 indexId; - - /** - * The number of columns of the composed index. - */ - uint8 numberColumns; - - /** - * The columns index of the composed index. - */ - uint8* columns; - - /** - * The index itself. - */ - Index* index; -}; - -/** - * The information stored for each query concerning the temporary tables size. - */ -struct MemoryUsageEntry -{ - /** - * The hash code key. - */ - int32 key; - - /** - * Temporary .db size. - */ - int32 dbSize; - - /** - * Temporary .dbo size. - */ - int32 dboSize; - - /** - * The pointer to the next hash table entry. - */ - MemoryUsageEntry* next; -}; - -/** - * The hash table that stores the information for each query concerning the temporary tables size. - */ -struct MemoryUsageHT -{ - /** - * The information matrix. - */ - MemoryUsageEntry** items; - - /** - * The hash table size. - */ - int32 size; - - /** - * Used to mask the hash key. - */ - int32 hash; - - /** - * The capacity. - */ - int32 threshold; -}; - -// juliana@227_20: corrected order by or group by with strings being too slow. -/** - * An structure used to sort tables with strings. - */ -struct StringArray -{ - /* - * The length of the loaded string. - */ - int32 length; - - /* - * The string loaded. - */ - JCharP string; -}; - -#ifdef ENABLE_TEST_SUITE -typedef struct TestSuite TestSuite; -#endif - -#endif diff --git a/LitebaseSDK/src/native/Macros.h b/LitebaseSDK/src/native/Macros.h deleted file mode 100644 index f07df27f62..0000000000 --- a/LitebaseSDK/src/native/Macros.h +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This file defines the macros used by Litebase. - */ - -#ifndef LITEBASE_MACROS_H -#define LITEBASE_MACROS_H - -/** - * The following ifdef block is the standard way of creating macros which makes exporting from a DLL simpler. All files within this DLL are compiled - * with the TESTE_EXPORTS symbol defined on the command line. This symbol should not be defined on any project that uses this DLL. This way any other - * project whose source files include this file see TESTE_API functions as being imported from a DLL, wheras this DLL sees symbols defined with this - * macro as being exported. - */ -#if defined (WIN32) - #ifdef LB_EXPORTS - #define LB_API __declspec(dllexport) - #else - #define LB_API __declspec(dllimport) - #endif -#else - #define LB_API extern -#endif - -// Macro to trace Litebase function calls. This should be used in the beginning of every function. -#ifdef ENABLE_TRACE - #define TRACE(x) bool unused = TC_trace(x); -#else - #define TRACE(x) -#endif - -// Macros for path separators. -#if defined (WIN32) - #define PATH_SEPARATOR '\\' - #define NO_PATH_SEPARATOR '/' -#else - #define PATH_SEPARATOR '/' - #define NO_PATH_SEPARATOR '\\' -#endif - -// fdie -// juliana@202_24: removed problem with double processing. -// juliana@210_6: removed problem with long processing. -// Supports ARM's double mixed endianness. This is handled by the READ_DOUBLE macro. The GCCE compiler of the new Symbian S60v3 generates non mixed -// endianness doubles. - #define READ_DOUBLE(destination, source) xmemmove(destination, source, 8) - -// Classes fields. -// DriverException -#define OBJ_DriverExceptionCause(o) FIELD_OBJ(o, OBJ_CLASS(o), 0) // DriverException.cause - -// LitebaseConnection -#define OBJ_LitebaseIsAscii(o) FIELD_I32(o, 0) // LitebaseConnection.isAscii -#define OBJ_LitebaseUseCrypto(o) FIELD_I32(o, 1) // LitebaseConnection.useCrypto -#define OBJ_LitebaseDontFinalize(o) FIELD_I32(o, 2) // LitebaseConnection.dontFinalize -#define OBJ_LitebaseKey(o) FIELD_I32(o, 3) // LitebaseConnection.key -#define OBJ_LitebaseAppCrid(o) FIELD_I32(o, 4) // LitebaseConnection.appCrid - -// LitebaseConnection.htTables -#define getLitebaseHtTables(o) ((Hashtable*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 0)) -#define setLitebaseHtTables(o, v) (FIELD_I64(o, OBJ_CLASS(o), 0) = (size_t)v) - -// LitebaseConnection.sourcePath -#define getLitebaseSourcePath(o) ((TCHARP)(size_t)FIELD_I64(o, OBJ_CLASS(o), 1)) -#define setLitebaseSourcePath(o, v) (FIELD_I64(o, OBJ_CLASS(o), 1) = (size_t)v) - -// LitebaseConnection.htPS // juliana@226_16 -#define getLitebaseHtPS(o) ((Hashtable*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 2)) -#define setLitebaseHtPS(o, v) (FIELD_I64(o, OBJ_CLASS(o), 2) = (size_t)v) - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -// LitebaseConnection.nodes -#define getLitebaseNodes(o) ((int32*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 3)) -#define setLitebaseNodes(o, v) (FIELD_I64(o, OBJ_CLASS(o), 3) = (size_t)v) - -// PreparedStatement -#define OBJ_PreparedStatementType(o) FIELD_I32(o, 0) // PreparedStatement.type -#define OBJ_PreparedStatementStoredParams(o) FIELD_I32(o, 1) // PreparedStatement.storedParams -#define OBJ_PreparedStatementDontFinalize(o) FIELD_I32(o, 2) // PreparedStatement.dontFinalize -#define OBJ_PreparedStatementSqlExpression(o) FIELD_OBJ(o, OBJ_CLASS(o), 0) // PreparedStatement.sqlExpression -#define OBJ_PreparedStatementDriver(o) FIELD_OBJ(o, OBJ_CLASS(o), 1) // PreparedStatement.driver -#define OBJ_PreparedStatementObjParams(o) FIELD_OBJ(o, OBJ_CLASS(o), 2) // PreparedStatement.ObjParams // juliana@222_8 - -// PreparedStatement.paramsAsStrs -#define getPreparedStatementParamsAsStrs(o) ((JCharP*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 0)) -#define setPreparedStatementParamsAsStrs(o, v) (FIELD_I64(o, OBJ_CLASS(o), 0) = (size_t)v) - -// PreparedStatement.paramsPos -#define getPreparedStatementParamsPos(o) ((int16*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 1)) -#define setPreparedStatementParamsPos(o, v) (FIELD_I64(o, OBJ_CLASS(o), 1) = (size_t)v) - -// PreparedStatement.paramsLength -#define getPreparedStatementParamsLength(o) ((int16*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 2)) -#define setPreparedStatementParamsLength(o, v) (FIELD_I64(o, OBJ_CLASS(o), 2) = (size_t)v) - -// PreparedStatement.statement -#define getPreparedStatementStatement(o) ((size_t)FIELD_I64(o, OBJ_CLASS(o), 3)) -#define setPreparedStatementStatement(o, v) (FIELD_I64(o, OBJ_CLASS(o), 3) = (size_t)v) - -// ResultSet -#define OBJ_ResultSetDontFinalize(o) FIELD_I32(o, 0) // ResultSet.dontFinalize - -// ResultSet.bag -#define getResultSetBag(o) ((ResultSet*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 0)) -#define setResultSetBag(o, v) (FIELD_I64(o, OBJ_CLASS(o), 0) = (size_t)v) - -// ResultSetMetaData -#define OBJ_ResultSetMetaData_ResultSet(o) FIELD_OBJ(o, OBJ_CLASS(o), 0) // ResultSetMetaData.resultSet - -// RowIterator -#define OBJ_RowIteratorRowid(o) FIELD_I32(o, 0) // RowIterator.rowid -#define OBJ_RowIteratorAttr(o) FIELD_I32(o, 1) // RowIterator.attr -#define OBJ_RowIteratorRowNumber(o) FIELD_I32(o, 2) // RowIterator.rowNumber - -// RowIterator.table -#define getRowIteratorTable(o) ((Table*)(size_t)FIELD_I64(o, OBJ_CLASS(o), 0)) -#define setRowIteratorTable(o, v) (FIELD_I64(o, OBJ_CLASS(o), 0) = (size_t)v) - -#define OBJ_RowIteratorData(o) FIELD_OBJ(o, OBJ_CLASS(o), 0) // RowIterator.data -#define OBJ_RowIteratorDriver(o) FIELD_OBJ(o, OBJ_CLASS(o), 1) // RowIterator.driver - -// Methods -#define loggerLog &loggerClass->methods[17] // Logger.log(int level, String message, boolean prependInfo) -#define loggerLogInfo &loggerClass->methods[18] // Logger.logInfo(StringBuffer message) // juliana@230_30 -#define addOutputHandler &loggerClass->methods[14] // Logger.addOutputHandler() -#define getLogger &loggerClass->methods[3] // Logger.getLogger() - -// Bitmap. // Sets all bits of a bitmap. -#define setBitOn(items, index) (items)[(index) >> 3] |= ((int32)1 << ((index) & 7)) // Sets a bit of a bitmap on. -#define setBitOff(items, index) (items)[(index) >> 3] &= ~((int32)1 << ((index) & 7)) // Sets a bit of a bitmap off. -#define isBitSet(items, index) ((items)[(index) >> 3] & ((int32)1 << ((index) & 7))) != 0 // Verifies if a bit is set. -#define isBitUnSet(items, index) !(((items)[(index) >> 3] & ((int32)1 << ((index) & 7)))) // Verifies if a bit is unset. - -// Checks if a bit is set in a IntVector. -#define IntVectorisBitSet(v, index) ((v)->items[(index) >> 5] & ((int32)1 << ((index) & 31))) - -// Implements a stack using an IntVector or a ShortVector. -#define IntVectorPop(intVector) intVector.items[--intVector.size] // pop -#define IntVectorPush(intVector, value) IntVectorAdd(intVector, value) // push - -// Returns the number of bytes necessary to store null value information concerning the columns. Each column in a table corresponds to one bit. -#define NUMBEROFBYTES(colCount) (((colCount) + 7) >> 3) - -// Verifies if the column is defined as not null. -#define definedAsNotNull(byte) ((((byte) & ATTR_COLUMN_IS_NOT_NULL))) - -// Turns a string representing the application id of 4 characters into an integer. -#define getAppCridInt(cridStr) \ -(uint32)((uint32)((cridStr)[0]) << 24 | (uint32)((cridStr)[1]) << 16 | (uint32)((cridStr)[2]) << 8 | (uint32)((cridStr)[3])) - -// Parser macros. -#define YYPARSE_PARAM parser // yyparse() parameter. -#define parserTP ((LitebaseParser*)parser) // Typed parameter. -#define YYTRANSLATE(YYX) ((uint32)(YYX) <= YYMAXUTOK? yytranslate[YYX] : YYUNDEFTOK) // Translates YYLEX symbols. // Pops parser stacks. - -// Gets the current character. -#define GET_YYCURRENT(yycurrent) yycurrent = parser->yyposition < parser->length ? zzReaderChars[parser->yyposition++] : PARSER_EOF - -// Inserts a character in the buffer of the current token. -#define INSERT_CHAR(yycurrent) yycurrent = parser->yyposition++ < parser->length ? zzReaderChars[parser->yyposition - 1] : PARSER_EOF - -// Updates the hash code of a sql select command. -#define CALCULATE_HASH(character) if (parser->yyposition > 0) hash = (hash << 5) - hash + (int32)(character) - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -// Key macros. -// Indicates if two keys are equal. -#define keyEquals(context, key1, key2, size, plainDB) (key2 && !keyCompareTo(context, key1, key2, size, plainDB)) - -// Indicates if a node of an index is a leaf node. -#define nodeIsLeaf(node) (*node->children == LEAF) - -// Linked list declaration. -#define TC_DeclareList(type) \ -typedef struct type##s \ -{ \ - struct type##s* next; \ - struct type##s* prev; \ - type value; \ -} type##s; \ -type##s* TC_##type##sAdd(type##s* list, type value, Heap h); \ -type##s* TC_##type##sRemove(type##s* list, type value) \ - -// Linked list declaration, with functions to add and remove an element. -// The function to add an element searchs the list to see if there is a free node to add the element before alocating anything. -// The funtion to remove an element only invalidates the list position, which can be re-used later on. -// juliana@221_1: solved a problem that could reduce the free memory too much if many prepared statements were created and collected many times. -#define TC_ImplementList(type) \ -type##s* TC_##type##sAdd(type##s* list, type value, Heap heap) \ -{ \ - if (!list) \ - { \ - (list = (type##s*)TC_heapAlloc(heap, sizeof(type##s)))->value = value; \ - list->next = list->prev = list; \ - } \ - else \ - { \ - type##s* head = list; \ - type##s *element; \ - do \ - { \ - if (!head->value) \ - { \ - head->value = value; \ - return list; \ - } \ - head = head->next; \ - } while (head != list); \ - (element = (type##s*)TC_heapAlloc(heap,sizeof(type##s)))->value = value; \ - element->prev = list->prev; \ - element->next = list; \ - list->prev =list->prev->next = element; \ - } \ - return list; \ -} \ - \ -type##s* TC_##type##sRemove(type##s* list, type value) \ -{ \ - type##s* head = list; \ - if (head) \ - do \ - { \ - if (list->value == value) \ - list->value = null; \ - list = list->next; \ - } while (head != list); \ - return head; \ -} -#endif diff --git a/LitebaseSDK/src/native/MarkBits.c b/LitebaseSDK/src/native/MarkBits.c deleted file mode 100644 index c624fe2d60..0000000000 --- a/LitebaseSDK/src/native/MarkBits.c +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * These functions generate the result set indexed rows map from the associated table indexes applied to the associated WHERE clause. They should - * only be used if the result set has a WHERE clause. - */ - -#include "MarkBits.h" - -/** - * Resets the object and the bitmap. - * - * @param markBits The mark bits to be reseted. - * @param bits A new bitmap for the mark bits. - */ -void markBitsReset(MarkBits* markBits, IntVector* bits) -{ - TRACE("markBitsReset") - markBits->isNoLongerEqual = false; - markBits->bitValue = true; - markBits->indexBitmap = bits; - xmemzero(bits->items, bits->size << 2); -} - -/** - * Climbs on a key. - * - * @param context The thread context where the function is being executed. - * @param key The key to be climbed on. - * @param markBits The rows which will be returned to the result set. - * @return false if the key could be climbed; -1 if an error occurs, or true, otherwise. - */ -int32 onKey(Context context, Key* key, MarkBits* markBits) -{ - TRACE("onKey") - Key* leftKey = &markBits->leftKey; - SQLValue* keys0 = key->keys; - Index* index = key->index; - Table* table = index->table; - PlainDB* plainDB = &table->db; - XFile* dbo = &plainDB->dbo; - int32 numberColumns = index->numberColumns, - leftOp = *markBits->leftOp, - rightOp = *markBits->rightOp, - length = 0, - size = *index->colSizes; - - // juliana@230_2: solved a possible crash with LIKE "...%" - if (!keys0->length && size) // A strinhg may not be loaded. - { - nfSetPos(dbo, keys0->asInt); // Gets and sets the string position in the .dbo. - - // Fetches the string length and the string itself. - if (!nfReadBytes(context, dbo, (uint8*)&length, 2) || !loadString(context, plainDB, keys0->asChars, keys0->length = length)) - return -1; - } - - if (markBits->rightKey.index) - { - int32 comp = keyCompareTo(null, key, &markBits->rightKey, numberColumns, null); - if (rightOp == OP_REL_LESS_EQUAL && comp > 0) // If key <= right key, stops. - return false; - if (rightOp == OP_REL_LESS && comp >= 0) // if key < right key, stops. - return false; - } - - // For inclusion operations, just uses the value. - if (leftOp == OP_REL_EQUAL || leftOp == OP_REL_GREATER_EQUAL || (leftOp == OP_REL_GREATER && markBits->isNoLongerEqual)) - onValue(key->record, markBits); // Climbs on the value. - else if (leftOp == OP_REL_GREATER) // The key can still be equal. - { - if (keyCompareTo(null, leftKey, key, numberColumns, null)) - { - markBits->isNoLongerEqual = true; - onValue(key->record, markBits); // Climbs on the value. - } - } - else // OP_PAT_MATCH_LIKE - { - JCharP patStr, - valStr; - JChar dateTimeBuf16[24]; - int32 valLen, - type = *index->types; - bool caseless = type == CHARS_NOCASE_TYPE; - - // juliana@230_3: corrected a bug of LIKE using DATE and DATETIME not returning the correct result. - if (type == DATE_TYPE) - { - int32 asDate = keys0->asInt; - date2JCharP(asDate / 10000, asDate / 100 % 100, asDate % 100, valStr = dateTimeBuf16); - valLen = 10; - } - else if (type == DATETIME_TYPE) - { - int32 asDate = keys0->asDate, - asTime = keys0->asTime; - dateTime2JCharP(asDate / 10000, asDate / 100 % 100, asDate % 100, - asTime / 10000000, asTime / 100000 % 100, asTime / 1000 % 100, asTime % 1000, valStr = dateTimeBuf16); - valLen = 23; - } - else - { - valStr = keys0->asChars; - valLen = keys0->length; - } - - patStr = (keys0 = leftKey->keys)->asChars; - if (str16StartsWith(valStr, patStr, valLen, keys0->length, 0, caseless)) // Only starts with are used with indices. - onValue(key->record, markBits); // climb on the value. - else - return false; - } - return true; // Does not visit this value, but continues the search. -} - -/** - * Climbs on a value. - * - * @param record The record value to be climbed on. - * @param markBits The rows which will be returned to the result set. - */ -void onValue(int32 record, MarkBits* markBits) -{ - TRACE("onValue") - if (record != NO_VALUE) - { - if (markBits->bitValue) - markBits->indexBitmap->items[record >> 5] |= ((int32)1 << (record & 31)); // set - else - markBits->indexBitmap->items[record >> 5] &= ~((int32)1 << (record & 31)); // reset - } -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests the correctnes of markBitsOnValue(). - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(markBitsOnValue) -{ - MarkBits markBits; - Heap heap = heapCreate(); - IntVector vector; - int32 i = 8192; - int32* array; - UNUSED(currentContext) - IF_HEAP_ERROR(heap) - { - heapDestroy(heap); - TEST_FAIL(tc, "OutOfMemoryError"); - goto finish; - } - - // Initializes the structures. - xmemzero(&markBits, sizeof(MarkBits)); - xmemzero(&vector, sizeof(IntVector)); - array = TC_heapAlloc(heap, 32768); - vector.length = 8192; - vector.items = array; - markBitsReset(&markBits, &vector); - - // Tests bit set and reset. - while ((i -= 4) >= 0) - { - markBits.bitValue = 1; - onValue(i, &markBits); - ASSERT1_EQUALS(True, IntVectorisBitSet(markBits.indexBitmap, i)); - markBits.bitValue = 0; - onValue(i, &markBits); - ASSERT1_EQUALS(False, IntVectorisBitSet(markBits.indexBitmap, i)); - } - - heapDestroy(heap); -finish : ; -} - -/** - * Tests the correctnes of markBitsReset(). - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(markBitsReset) -{ - MarkBits markBits; - IntVector vector; - int32 array[3]; - UNUSED(currentContext) - - // Initializes the structures. - xmemzero(&markBits, sizeof(MarkBits)); - xmemzero(&vector, sizeof(IntVector)); - xmemzero(array, 12); - vector.length = 3; - vector.items = array; - - // MarkBits is initialized with an empty vector. - markBitsReset(&markBits, &vector); - ASSERT1_EQUALS(True, markBits.bitValue); - ASSERT1_EQUALS(False, markBits.isNoLongerEqual); - ASSERT2_EQUALS(I32, 3, markBits.indexBitmap->length); - ASSERT2_EQUALS(I32, 0, markBits.indexBitmap->size); - - // MarkBits is initialized with an unary vector. - vector.items[0] = 1; - vector.size = 1; - markBitsReset(&markBits, &vector); - ASSERT1_EQUALS(True, markBits.bitValue); - ASSERT1_EQUALS(False, markBits.isNoLongerEqual); - ASSERT2_EQUALS(I32, 3, markBits.indexBitmap->length); - ASSERT2_EQUALS(I32, 1, markBits.indexBitmap->size); - - // MarkBits is initialized with a binary vector. - vector.items[0] = vector.items[1] = 2; - vector.size = 2; - markBitsReset(&markBits, &vector); - ASSERT1_EQUALS(True, markBits.bitValue); - ASSERT1_EQUALS(False, markBits.isNoLongerEqual); - ASSERT2_EQUALS(I32, 3, markBits.indexBitmap->length); - ASSERT2_EQUALS(I32, 2, markBits.indexBitmap->size); - - // MarkBits is initialized with a ternary vector. - vector.items[0] = vector.items[1] = vector.items[2] = 2; - vector.size = 3; - markBitsReset(&markBits, &vector); - ASSERT1_EQUALS(True, markBits.bitValue); - ASSERT1_EQUALS(False, markBits.isNoLongerEqual); - ASSERT2_EQUALS(I32, 3, markBits.indexBitmap->length); - ASSERT2_EQUALS(I32, 3, markBits.indexBitmap->size); - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/MarkBits.h b/LitebaseSDK/src/native/MarkBits.h deleted file mode 100644 index 84b8459ff9..0000000000 --- a/LitebaseSDK/src/native/MarkBits.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * These functions generate the result set indexed rows map from the associated table indexes applied to the associated WHERE clause. They should - * only be used if the result set has a WHERE clause. - */ - -#ifndef LITEBASE_MARKBITS_H -#define LITEBASE_MARKBITS_H - -#include "Litebase.h" - -/** - * Resets the object and the bitmap. - * - * @param markBits The mark bits to be reseted. - * @param bits A new bitmap for the mark bits. - */ -void markBitsReset(MarkBits* markBits, IntVector* bits); - -/** - * Climbs on a key. - * - * @param context The thread context where the function is being executed. - * @param key The key to be climbed on. - * @param markBits The rows which will be returned to the result set. - * @return false if the key could be climbed; -1 if an error occurs, or true, otherwise. - */ -int32 onKey(Context context, Key* key, MarkBits* markBits); - -/** - * Climbs on a value. - * - * @param record The record value to be climbed on. - * @param markBits The rows which will be returned to the result set. - */ -void onValue(int32 record, MarkBits* markBits); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests the correctnes of markBitsOnValue(). - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_markBitsOnValue(TestSuite* testSuite, Context currentContext); - -/** - * Tests the correctnes of markBitsReset(). - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_markBitsReset(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/MemoryFile.c b/LitebaseSDK/src/native/MemoryFile.c deleted file mode 100644 index 8dc1c93d8e..0000000000 --- a/LitebaseSDK/src/native/MemoryFile.c +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines functions for a memory file, ie, a file that is allocated in memory and never dumped to disk. Used for result sets. - */ - -#include "MemoryFile.h" - -/** - * Sets the position in the memory file. - * - * @param xFile The memory file. - * @param position The new position in the buffer. - */ -void mfSetPos(XFile* xFile, int32 position) -{ - TRACE("mfSetPos") - xFile->position = position; -} - -/** - * Increases or shrinks the buffer for the memory file. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @param newSize the new size of the buffer. - * @return true if it was possible to allocate the new memory file buffer; false, otherwise. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - */ -bool mfGrowTo(Context context, XFile* xFile, uint32 newSize) -{ - TRACE("mfGrowTo") - if ((xFile->fbuf = xrealloc(xFile->fbuf, newSize))) - { - xFile->size = newSize; - return true; - } - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; -} - -/** - * Reads bytes from the buffers. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @param buffer The byte array to read data into. - * @param count The number of bytes to read. - * @return true. - */ -bool mfReadBytes(Context context, XFile* xFile, uint8* buffer, int32 count) -{ - TRACE("mfReadBytes") - UNUSED(context) - xmemmove(buffer, &xFile->fbuf[xFile->position], count); - xFile->position += count; - return true; -} - -/** - * Writes bytes into the buffer. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @param buffer The byte array to write data from. - * @param count The number of bytes to write. - * @return true. - */ -bool mfWriteBytes(Context context, XFile* xFile, uint8* buffer, int32 count) -{ - TRACE("mfWriteBytes") - UNUSED(context) - xmemmove(&xFile->fbuf[xFile->position], buffer, count); - xFile->position += count; - return true; -} - -/** - * Closes a memory file, by freeing its memory buffers. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @return Always true. It has a return value in order to be compatible with nfClose(). - */ -bool mfClose(Context context, XFile* xFile) -{ - TRACE("mfClose") - UNUSED(context) - xfree(xFile->fbuf); - return true; -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if mfClose() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(mfClose) -{ - XFile file; - int32 i = 2097152; - - file.fbuf = null; // Initializes the buffer. - - while ((i -= 2048) >= 0) // Tests if the buffer is really created and destroyed. - { - mfGrowTo(currentContext, &file, i); - mfClose(currentContext, &file); - ASSERT1_EQUALS(Null, file.fbuf); - } - -finish: ; -} - -/** - * Tests if mfGrowTo() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(mfGrowTo) -{ - XFile file; - int32 i = 2097152; - - file.fbuf = null; // Initializes the buffer. - - while ((i -= 2048) >= 0) // Tests if the buffer is really grown. - { - mfGrowTo(currentContext, &file, i); - ASSERT2_EQUALS(I32, i, file.size); - } - - xfree(file.fbuf); // Destroys the buffer. - -finish: ; -} - -/** - * Tests if mfReadBytes() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(mfReadBytes) -{ - XFile file; - int32 i = 2097152, - count = 0; - uint8* buffer1; - uint8* buffer2; - - // Allocates the buffers. - buffer1 = xmalloc(4096); - buffer2 = xmalloc(4096); - if (!buffer1 || !buffer2) - { - xfree(buffer1); - xfree(buffer2); - TEST_FAIL(tc, "OutOfMemoryError"); - goto finish; - } - - // Initializes the memory file. - file.fbuf = null; - file.position = 0; - mfGrowTo(currentContext, &file, 16384); - - while ((i -= 2048) >= 0) // Writes data. - { - xmemset(buffer1, i % 255, count); - ASSERT1_EQUALS(True, mfWriteBytes(currentContext, &file, buffer1, count)); - mfSetPos(&file, count); - ASSERT1_EQUALS(True, mfReadBytes(currentContext, &file, buffer1, count)); - xmemset(buffer2, i % 255, count); - ASSERT3_EQUALS(Block, buffer1, buffer2, count); - mfSetPos(&file, ++count); - } - - // Destroys the buffers. - xfree(file.fbuf); - xfree(buffer1); - xfree(buffer2); - -finish: ; -} - -/** - * Tests if mfSetPos() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(mfSetPos) -{ - XFile file; - int32 i = 2097152; - UNUSED(currentContext) - - while ((i -= 2048) >= 0) // Tests if the memory file position is really set. - { - mfSetPos(&file, i); - ASSERT2_EQUALS(I32, i, file.position); - } - -finish: ; -} - -/** - * Tests if mfWriteBytes() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(mfWriteBytes) -{ - XFile file; - int32 i = 2097152, - count = 0; - uint8* buffer1; - uint8* buffer2; - - // Allocates the buffers. - buffer1 = xmalloc(4096); - buffer2 = xmalloc(4096); - if (!buffer1 || !buffer2) - { - xfree(buffer1); - xfree(buffer2); - TEST_FAIL(tc, "OutOfMemoryError"); - goto finish; - } - - // Initializes the memory file. - file.fbuf = null; - file.position = 0; - mfGrowTo(currentContext, &file, 16384); - - while ((i -= 2048) >= 0) // Writes data. - { - xmemset(buffer1, i % 255, count); - ASSERT1_EQUALS(True, mfWriteBytes(currentContext, &file, buffer1, count)); - mfSetPos(&file, count); - ASSERT1_EQUALS(True, mfReadBytes(currentContext, &file, buffer1, count)); - xmemset(buffer2, i % 255, count); - ASSERT3_EQUALS(Block, buffer1, buffer2, count); - mfSetPos(&file, ++count); - } - - // Destroys the buffers. - xfree(file.fbuf); - xfree(buffer1); - xfree(buffer2); - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/MemoryFile.h b/LitebaseSDK/src/native/MemoryFile.h deleted file mode 100644 index bbb46c1d03..0000000000 --- a/LitebaseSDK/src/native/MemoryFile.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions for a memory file, ie, a file that is allocated in memory and never dumped to disk. Used for result sets. - */ - -#ifndef LITEBASE_MEMORY_FILE_H -#define LITEBASE_MEMORY_FILE_H - -#include "Litebase.h" - -/** - * Sets the position in the memory file. - * - * @param xFile The memory file. - * @param position The new position in the buffer. - */ -void mfSetPos(XFile* xFile, int32 position); - -/** - * Increases or shrinks the buffer for the memory file. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @param newSize the new size of the buffer. - * @return true if it was possible to allocate the new memory file buffer; false, otherwise. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - */ -bool mfGrowTo(Context context, XFile* xFile, uint32 newSize); - -/** - * Reads bytes from the buffers. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @param buffer The byte array to read data into. - * @param count The number of bytes to read. - * @return true. - */ -bool mfReadBytes(Context context, XFile* xFile, uint8* buffer, int32 count); - -/** - * Writes bytes into the buffer. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @param buffer The byte array to write data from. - * @param count The number of bytes to write. - * @return true. - */ -bool mfWriteBytes(Context context, XFile* xFile, uint8* buffer, int32 count); - -/** - * Closes a memory file, by freeing its memory buffers. - * - * @param context The thread context where the function is being executed. - * @param xFile The memory file. - * @return Always true. It has a return value in order to be compatible with nfClose(). - */ -bool mfClose(Context context, XFile* xFile); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if mfClose() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_mfClose(TestSuite* testSuite, Context currentContext); - -/** - * Tests if mfGrowTo() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_mfGrowTo(TestSuite* testSuite, Context currentContext); - -/** - * Tests if mfReadBytes() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_mfReadBytes(TestSuite* testSuite, Context currentContext); - -/** - * Tests if mfSetPos() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_mfSetPos(TestSuite* testSuite, Context currentContext); - -/** - * Tests if mfWriteBytes() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_mfWriteBytes(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/NativeMethods.c b/LitebaseSDK/src/native/NativeMethods.c deleted file mode 100644 index 7c9b77acb5..0000000000 --- a/LitebaseSDK/src/native/NativeMethods.c +++ /dev/null @@ -1,5557 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines Litebase native methods. - */ - -#include "NativeMethods.h" - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Moves to the next record and fills the data members. - * - * @param p->obj[0] The row iterator. - * @param p->retI Receives true if it is possible to iterate to the next record. Otherwise, it will return false. - */ -LB_API void lRI_next(NMParams p) // litebase/RowIterator public native boolean next(); -{ - TRACE("lRI_next") - - MEMORY_TEST_START - - // juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a - // DriverException. - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - if (testRIClosed(p)) - { - TCObject rowIterator = p->obj[0]; - Table* table = getRowIteratorTable(rowIterator); - int32 rowNumber = OBJ_RowIteratorRowNumber(rowIterator), - id; - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - - if (++rowNumber < plainDB->rowCount && plainRead(p->currentContext, plainDB, rowNumber)) - { - xmove4(&id, basbuf); - xmemmove((uint8*)ARRAYOBJ_START(OBJ_RowIteratorData(rowIterator)), basbuf, plainDB->rowSize); - OBJ_RowIteratorRowid(rowIterator) = id & ROW_ID_MASK; - OBJ_RowIteratorAttr(rowIterator) = ((id & ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3; // Masks out the attributes. - p->retI = true; - } - else - p->retI = false; - OBJ_RowIteratorRowNumber(rowIterator) = rowNumber; - - // juliana@223_5: now possible null values are treated in RowIterator. - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Moves to the next record with an attribute different of SYNCED. - * - * @param p->obj[0] The row iterator. - * @param p->retI Receives true if it is possible to iterate to a next record not synced. Otherwise, it will return false. - */ -LB_API void lRI_nextNotSynced(NMParams p) // litebase/RowIterator public native boolean nextNotSynced(); -{ - TRACE("lRI_nextNotSynced") - - MEMORY_TEST_START - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - if (testRIClosed(p)) - { - TCObject rowIterator = p->obj[0]; - Context context = p->currentContext; - Table* table = getRowIteratorTable(rowIterator); - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - int32 rowNumber = OBJ_RowIteratorRowNumber(rowIterator), - rowSize = plainDB->rowSize, - id; - - p->retI = false; - - while (++rowNumber < plainDB->rowCount && plainRead(context, plainDB, rowNumber)) - { - xmove4(&id, basbuf); - if ((id & ROW_ATTR_MASK) == ROW_ATTR_SYNCED) - continue; - xmemmove((uint8*)ARRAYOBJ_START(OBJ_RowIteratorData(rowIterator)), basbuf, rowSize); - OBJ_RowIteratorRowid(rowIterator) = id & ROW_ID_MASK; - OBJ_RowIteratorAttr(rowIterator) = ((id & ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3; // Masks out the attributes. - p->retI = true; - break; - } - OBJ_RowIteratorRowNumber(rowIterator) = rowNumber; - - // juliana@223_5: now possible null values are treated in RowIterator. - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * If the attribute is currently NEW or UPDATED, this method sets them to SYNCED. Note that if the row is DELETED, the change will be ignored. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_setSynced(NMParams p) // litebase/RowIterator public native void setSynced(); -{ - TRACE("lRI_setSynced") - - MEMORY_TEST_START - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - if (testRIClosed(p)) - { - TCObject rowIterator = p->obj[0]; - Table* table = getRowIteratorTable(rowIterator); - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - int32 rowNumber = OBJ_RowIteratorRowNumber(rowIterator), - id, - oldAttr, - newAttr; - - // The record is assumed to have been already read. - xmove4(&id, basbuf); - - // guich@560_19 // juliana@230_16: solved a bug with row iterator. - if ((newAttr = OBJ_RowIteratorAttr(rowIterator) = ROW_ATTR_SYNCED_MASK) != (oldAttr = ((id & ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3) - && oldAttr != (int32)ROW_ATTR_DELETED_MASK) - { - id = (id & ROW_ID_MASK) | newAttr; // Sets the new attribute. - xmove4(basbuf, &id); - plainRewrite(p->currentContext, plainDB, rowNumber); - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@270_29: added RowIterator.setNotSynced(). -/** - * Forces the attribute to be NEW. This method will be useful if a row was marked as synchronized but was not sent to server for some problem. - * If the row is marked as DELETED, its attribute won't be changed. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_setNotSynced(NMParams p) // litebase/RowIterator public native void setNotSynced(); -{ - TRACE("lRI_setNotSynced") - - MEMORY_TEST_START - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - if (testRIClosed(p)) - { - TCObject rowIterator = p->obj[0]; - Table* table = getRowIteratorTable(rowIterator); - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - int32 rowNumber = OBJ_RowIteratorRowNumber(rowIterator), - id, - oldAttr, - newAttr; - - // The record is assumed to have been already read. - xmove4(&id, basbuf); - - // guich@560_19 // juliana@230_16: solved a bug with row iterator. - if ((newAttr = OBJ_RowIteratorAttr(rowIterator) = ROW_ATTR_NEW) != (oldAttr = ((id & ROW_ATTR_MASK) >> ROW_ATTR_SHIFT) & 3) - && oldAttr != (int32)ROW_ATTR_DELETED_MASK) - { - id = (id & ROW_ID_MASK) | newAttr; // Sets the new attribute. - xmove4(basbuf, &id); - plainRewrite(p->currentContext, plainDB, rowNumber); - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Closes this iterator. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_close(NMParams p) // litebase/RowIterator public native void close(); -{ - TRACE("lRI_close") - - MEMORY_TEST_START - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - if (testRIClosed(p)) - { - TCObject rowIterator = p->obj[0]; - - // juliana@227_22: RowIterator.close() now flushes the setSynced() calls. - XFile* dbFile = &getRowIteratorTable(rowIterator)->db.db; - if (dbFile->cacheIsDirty) - flushCache(p->currentContext, dbFile); - - setRowIteratorTable(rowIterator, null); - OBJ_RowIteratorData(rowIterator) = null; - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Resets the counter to zero so it is possible to restart to fetch records. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_reset(NMParams p) // litebase/RowIterator public native void reset(); -{ - TRACE("lRI_reset") - MEMORY_TEST_START - OBJ_RowIteratorRowNumber(p->obj[0]) = -1; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a short contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The short column index, starting from 1. - * @param p->retI Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getShort_i(NMParams p) // litebase/RowIterator public native short getShort(int column); -{ - TRACE("lRI_getShort_i") - MEMORY_TEST_START - getByIndex(p, SHORT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns an integer contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The integer column index, starting from 1. - * @param p->retI Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getInt_i(NMParams p) // litebase/RowIterator public native int getInt(int column); -{ - TRACE("lRI_getInt_i") - MEMORY_TEST_START - getByIndex(p, INT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a long integer contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The long integer column index, starting from 1. - * @param p->retL Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getLong_i(NMParams p) // litebase/RowIterator public native long getLong(int column); -{ - TRACE("lRI_getLong_i") - MEMORY_TEST_START - getByIndex(p, LONG_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a floating point number contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The floating point number column index, starting from 1. - * @param p->retD Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getFloat_i(NMParams p) // litebase/RowIterator public native double getFloat(int column); -{ - TRACE("lRI_getFloat_i") - MEMORY_TEST_START - getByIndex(p, FLOAT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a double precision floating point number contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The double precision floating point number column index, starting from 1. - * @param p->retD Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getDouble_i(NMParams p) // litebase/RowIterator public native double getDouble(int column); -{ - TRACE("lRI_getDouble_i") - MEMORY_TEST_START - getByIndex(p, DOUBLE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a string contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The string column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getString_i(NMParams p) // litebase/RowIterator public native String getString(int column); -{ - TRACE("lRI_getString_i") - MEMORY_TEST_START - getByIndex(p, CHARS_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a blob contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The blob column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getBlob_i(NMParams p) // litebase/RowIterator public native byte[] getBlob(int column); -{ - TRACE("lRI_getBlob_i") - MEMORY_TEST_START - getByIndex(p, BLOB_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Returns a date contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The date column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getDate_i(NMParams p) // litebase/RowIterator public native totalcross.util.Date getDate(int column); -{ - TRACE("lRI_getDate_i") - MEMORY_TEST_START - getByIndex(p, DATE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Retur - ns a datetime contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The datetime column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getDateTime_i(NMParams p) // litebase/RowIterator public native totalcross.sys.Time getDateTime(int column); -{ - TRACE("lRI_getDateTime_i") - MEMORY_TEST_START - getByIndex(p, DATETIME_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. -// juliana@223_5: now possible null values are treated in RowIterator. -/** - * Indicates if this column has a NULL. - * - * @param p->i32[0] The column index, starting from 1. - * @param p->retI Receives true if the value is SQL NULL; false, otherwise. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRI_isNull_i(NMParams p) // litebase/RowIterator public native boolean isNull(int column) IllegalArgumentException; -{ - TRACE("lRI_isNull_i") - - MEMORY_TEST_START - - // juliana@225_14: RowIterator must throw an exception if its driver is closed. - if (testRIClosed(p)) - { - TCObject rowIterator = p->obj[0]; - Table* table = getRowIteratorTable(rowIterator); - int32 column = p->i32[0]; - - if (column < 0 || column >= table->columnCount) // Checks if the column index is within range. - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER), column); - else - p->retI = isBitSet(table->columnNulls, column); // juliana@223_5: now possible null values are treated in RowIterator. - } - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Creates a Litebase connection for the default creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param p->retO Receives a Litebase instance. - */ -LB_API void lLC_privateGetInstance(NMParams p) // litebase/LitebaseConnection public static native litebase.LitebaseConnection privateGetInstance(); -{ - TRACE("lLC_privateGetInstance") - MEMORY_TEST_START - TC_setObjectLock(p->retO = create(p->currentContext, TC_getApplicationId(), null), UNLOCKED); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public static native litebase.LitebaseConnection privateGetInstance(String appCrid) throws DriverException, -// NullPointerException; -/** - * Creates a Litebase connection for the given creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param p->obj[0] The creator id, which may (or not) be the same one of the current application and MUST be 4 characters long. - * @param p->retO Receives a Litebase instance. - * @throws DriverException If an application id with more or less than four characters is specified. - * @throws NullPointerException If appCrid == null. - */ -LB_API void lLC_privateGetInstance_s(NMParams p) -{ - TRACE("lLC_privateGetInstance_s") - char strAppId[5]; - TCObject appCrid = p->obj[0]; - - MEMORY_TEST_START - - if (!appCrid) // The application can't be null. - TC_throwNullArgumentException(p->currentContext, "appCrid"); - else if (String_charsLen(appCrid) != 4) // The application id must have 4 characters. - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_CRID)); - else - { - TC_JCharP2CharPBuf(String_charsStart(appCrid), 4, strAppId); - TC_setObjectLock(p->retO = create(p->currentContext, getAppCridInt(strAppId), null), UNLOCKED); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public static native litebase.LitebaseConnection privateGetInstance(String appCrid, String params) -// -// juliana@253_8: now Litebase supports weak cryptography. throws DriverException, NullPointerException; -/** - * Creates a connection with Litebase. - * - * @param p->obj[0] The creator id, which may be the same one of the current application. - * @param p->obj[1] Only the folder where it is desired to store the tables, null, if it is desired to use the current data - * path, or chars_type = chars_format; path = source_path[;crypto] , where chars_format can be ascii or - * unicode, source_path is the folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is passed as a parameter, unicode is used and there is no - * cryptography. Notice that path must be absolute, not relative. - *

    Note that databases belonging to multiple applications can be stored in the same path, since all tables are prefixed by the application's - * creator id. - *

    Also notice that to store Litebase files on card on Pocket PC, just set the second parameter to the correct directory path. - *

    It is not recommended to create the databases directly on the PDA. Memory cards are FIVE TIMES SLOWER than the main memory, so it will take - * a long time to create the tables. Even if the NVFS volume is used, it can be very slow. It is better to create the tables on the desktop, and copy - * everything to the memory card or to the NVFS volume. - *

    Due to the slowness of a memory card and the NVFS volume, all queries will be stored in the main memory; only tables and indexes will be stored - * on the card or on the NVFS volume. - *

    An exception will be raised if tables created with an ascii kind of connection are oppened with an unicode connection and vice-versa. - * @param p->retO Receives a Litebase instance. - * @throws DriverException If an application id with more or less than four characters is specified. - * @throws NullPointerException If appCrid == null. - */ -LB_API void lLC_privateGetInstance_ss(NMParams p) -{ - TRACE("lLC_privateGetInstance_ss") - char strAppId[5]; - TCObject appCrid = p->obj[0], - params = p->obj[1]; - - MEMORY_TEST_START - - if (!appCrid) // The application can't be null. - TC_throwNullArgumentException(p->currentContext, "appCrid"); - else if (String_charsLen(appCrid) != 4) // The application id must have 4 characters. - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_CRID)); - else - { - TC_JCharP2CharPBuf(String_charsStart(appCrid), 4, strAppId); - TC_setObjectLock(p->retO = create(p->currentContext, getAppCridInt(strAppId), params), UNLOCKED); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns the path where the tables created/opened by this connection are stored. - * - * @param p->obj[0] The connection with Litebase. - * @param p->retO Receives a string representing the path. - * @throws IllegalStateException If the driver is closed. - */ -LB_API void lLC_getSourcePath(NMParams p) // litebase/LitebaseConnection public native String getSourcePath() throws IllegalStateException; -{ - TRACE("lLC_getSourcePath") - TCObject driver = p->obj[0]; - - MEMORY_TEST_START - - if (OBJ_LitebaseDontFinalize(driver)) // The driver can't be closed. - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - else - TC_setObjectLock(p->retO = TC_createStringObjectFromTCHARP(p->currentContext, getLitebaseSourcePath(driver), -1), UNLOCKED); - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Used to execute a create table or create index SQL commands. - * - *

    Examples: - *

      - *
    • driver.execute("create table PERSON (NAME CHAR(30), SALARY DOUBLE, AGE INT, EMAIL CHAR(50))"); - *
    • driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - *
    - * - *

    When creating an index, its name is ignored but must be given. The index can be created after data was added to the table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL creation command. - */ -LB_API void lLC_execute_s(NMParams p) // litebase/LitebaseConnection public native void execute(String sql); -{ - TRACE("lLC_execute_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "sql")) // The sql can't be null and the driver can't be closed. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - sqlString = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - - if (logger) - { - LOCKVAR(log); - TC_executeMethod(context, loggerLog, logger, 16, sqlString, false); - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - litebaseExecute(context, driver, String_charsStart(sqlString), String_charsLen(sqlString)); - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Used to execute updates in a table (insert, delete, update, alter table, drop). E.g.: - * - *

    driver.executeUpdate("drop table person"); will drop also the indices. - *

    driver.executeUpdate("drop index * on person"); will drop all indices but not the primary key index. - *

    driver.executeUpdate("drop index name on person"); will drop the index for the "name" column. - *

    driver.executeUpdate("ALTER TABLE person DROP primary key"); will drop the primary key. - *

    driver.executeUpdate("update person set age=44, salary=3200.5 where name = 'guilherme campos hazan'"); will update the - * table. - *

    driver.executeUpdate("delete person where name like 'g%'"); will delete records of the table. - *

    driver.executeUpdate("insert into person (age, salary, name, email) - * values (32, 2000, 'guilherme campos hazan', 'guich@superwaba.com.br')"); will insert a record in the table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL update command. - * @param p->retI Receives the number of rows affected or 0 if a drop or alter operation was successful. - */ -LB_API void lLC_executeUpdate_s(NMParams p) // litebase/LitebaseConnection public native int executeUpdate(String sql); -{ - TRACE("lLC_executeUpdate_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "sql")) // The sql can't be null and the driver can't be closed. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - sqlString = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - - if (logger) - { - LOCKVAR(log); - TC_executeMethod(context, loggerLog, logger, 16, sqlString, false); - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - p->retI = litebaseExecuteUpdate(context, driver, String_charsStart(sqlString), String_charsLen(sqlString)); - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Used to execute queries in a table. Example: - * - *

    - * ResultSet rs = driver.executeQuery("select rowid, name, salary, age from person where age != 44");
    - * rs.afterLast();
    - * while (rs.prev())
    - *    Vm.debug(rs.getString(1) + ". " + rs.getString(2) + " - " + rs.getInt("age") + " years");
    - * 
    - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL query command. - * @param p->retO Receives a result set with the values returned from the query. - */ -LB_API void lLC_executeQuery_s(NMParams p) // litebase/LitebaseConnection public native litebase.ResultSet executeQuery(String sql); -{ - TRACE("lLC_executeQuery_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "sql")) // The sql can't be null and the driver can't be closed. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - sqlString = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) - { - LOCKVAR(log); - TC_executeMethod(context, loggerLog, logger, 16, sqlString, false); - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - TC_setObjectLock(p->retO = litebaseExecuteQuery(context, driver, String_charsStart(sqlString), String_charsLen(sqlString)), UNLOCKED); - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Creates a pre-compiled statement with the given sql. Prepared statements are faster for repeated queries. Instead of parsing the same query - * where only a few arguments change, it is better to create a prepared statement and the query is pre-parsed. Then, it is just needed to set the - * arguments (defined as ? in the sql) and run the sql. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL query command. - * @param p->retO Receives a pre-compiled SQL statement. - * @throws OutOfMemoryError If there is not enough memory to create the preparedStatement. - */ -LB_API void lLC_prepareStatement_s(NMParams p) // litebase/LitebaseConnection public native litebase.PrepareStatement prepareStatement(String sql) throws OutOfMemoryError; -{ - TRACE("lLC_prepareStatement_s") - MEMORY_TEST_START - - if (checkParamAndDriver(p, "sql")) // The sql can't be null and the driver can't be closed. - { - TCObject driver = p->obj[0], - sqlObj = p->obj[1], - oldSqlObj, - logger = litebaseConnectionClass->objStaticValues[1], - prepStmt = null; - Context context = p->currentContext; - Heap heapParser = null; - LitebaseParser* parse; - Hashtable* htPS; - JCharP sqlChars = String_charsStart(sqlObj), - sqlCharsAux; - char command[MAX_RESERVED_SIZE]; - int32 sqlLength = String_charsLen(sqlObj), - sqlLengthAux, - numParams = 0, - i, - hashCode; - bool isSelect = false; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "prepareStatement ") && TC_appendJCharP(context, logSBuffer, sqlChars, sqlLength)) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - // juliana@226_16: prepared statement is now a singleton. - // juliana@226a_21: solved a problem which could cause strange errors when using prepared statements. - htPS = getLitebaseHtPS(driver); - if ((prepStmt = p->retO = p->obj[0] = TC_htGetPtr(htPS, hashCode = TC_JCharPHashCode(sqlChars, sqlLength))) - && !OBJ_PreparedStatementDontFinalize(prepStmt) && (oldSqlObj = OBJ_PreparedStatementSqlExpression(prepStmt)) - && TC_JCharPEqualsJCharP(String_charsStart(oldSqlObj), sqlChars, String_charsLen(oldSqlObj), sqlLength)) - { - lPS_clearParameters(p); - goto finish; - } - - // The prepared statement. - if (!(prepStmt = p->retO = TC_createObject(context, "litebase.PreparedStatement"))) - goto finish; - OBJ_PreparedStatementDriver(prepStmt) = driver; - OBJ_PreparedStatementSqlExpression(prepStmt) = sqlObj; - - // Only parses commands that create statements. - sqlLengthAux = sqlLength; - sqlCharsAux = str16LeftTrim(sqlChars, &sqlLengthAux); - TC_CharPToLower(TC_JCharP2CharPBuf(sqlCharsAux, 8, command)); - if (!sqlLengthAux) // juliana@230_20 - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_SYNTAX_ERROR)); - goto finish; - } - - if (xstrstr(command, "create")) - OBJ_PreparedStatementType(p->retO) = CMD_CREATE_TABLE; - else if (xstrstr(command, "delete") || xstrstr(command, "insert") || (isSelect = (xstrstr(command, "select") != null)) || xstrstr(command, "update")) - { - bool locked = false; - Table* table; - - heapParser = heapCreate(); - IF_HEAP_ERROR(heapParser) - { - if (locked) - UNLOCKVAR(parser); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - -free: - heapDestroy(heapParser); - goto finish; - } - - // Parses the sql string. - locked = true; - LOCKVAR(parser); - parse = initLitebaseParser(context, sqlChars, sqlLength, isSelect, heapParser); - UNLOCKVAR(parser); - locked = false; - - // Error checking. - if (!parse) - goto free; - IF_HEAP_ERROR(heapParser) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto free; - } - - // juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first - // execution. - switch (parse->command) // Gets the command in the SQL expression and creates the apropriate statement. - { - case CMD_DELETE: - { - SQLDeleteStatement* deleteStmt = initSQLDeleteStatement(parse, true); - - if (litebaseBindDeleteStatement(context, driver, deleteStmt)) - { - SQLBooleanClause* whereClause = deleteStmt->whereClause; - - if (whereClause) - whereClause->expressionTreeBak = cloneTree(whereClause->expressionTree, null, heapParser); - - table = deleteStmt->rsTable->table; - IF_HEAP_ERROR(table->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto free; - } - OBJ_PreparedStatementType(prepStmt) = CMD_DELETE; - setPreparedStatementStatement(prepStmt, deleteStmt); - table->preparedStmts = TC_TCObjectsAdd(table->preparedStmts, prepStmt, table->heap); - } - else - goto free; - break; - } - - case CMD_INSERT: - { - SQLInsertStatement* insertStmt = initSQLInsertStatement(context, driver, parse); - - if (!insertStmt || !litebaseBindInsertStatement(context, insertStmt)) - goto free; - - OBJ_PreparedStatementType(prepStmt) = CMD_INSERT; - table = insertStmt->table; - IF_HEAP_ERROR(table->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto free; - } - setPreparedStatementStatement(prepStmt, insertStmt); - table->preparedStmts = TC_TCObjectsAdd(table->preparedStmts, prepStmt, table->heap); - break; - } - - case CMD_SELECT: - { - SQLSelectStatement* selectStmt = initSQLSelectStatement(parse, true); - - if (litebaseBindSelectStatement(context, driver, selectStmt)) - { - SQLSelectClause* selectClause = selectStmt->selectClause; - SQLResultSetTable** tableList = selectClause->tableList; - int32 len = selectClause->tableListSize; - SQLBooleanClause* whereClause = selectStmt->whereClause; - SQLColumnListClause* orderByClause = selectStmt->orderByClause; - SQLColumnListClause* groupByClause = selectStmt->groupByClause; - SQLResultSetField** fieldList; - uint8* fieldTableColIndexesBak; - Heap heap = selectClause->heap; - int32 count; - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto free; - } - - if (orderByClause) - { - fieldList = orderByClause->fieldList; - count = orderByClause->fieldsCount; - fieldTableColIndexesBak = orderByClause->fieldTableColIndexesBak = TC_heapAlloc(heap, count); - while (--count >= 0) - fieldTableColIndexesBak[count] = fieldList[count]->tableColIndex; - } - - // juliana@226_14: corrected a bug that would make a prepared statement with group by not work correctly after the first execution. - if (groupByClause) - { - fieldList = groupByClause->fieldList; - count = groupByClause->fieldsCount; - fieldTableColIndexesBak = groupByClause->fieldTableColIndexesBak = TC_heapAlloc(heap, count); - while (--count >= 0) - fieldTableColIndexesBak[count] = fieldList[count]->tableColIndex; - } - - if (whereClause) - whereClause->expressionTreeBak = cloneTree(whereClause->expressionTree, null, heapParser); - - OBJ_PreparedStatementType(prepStmt) = CMD_SELECT; - setPreparedStatementStatement(prepStmt, selectStmt); - selectStmt->selectClause->sqlHashCode = TC_JCharPHashCode(sqlChars, sqlLength); - while (--len >= 0) - { - table = tableList[len]->table; - IF_HEAP_ERROR(table->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto free; - } - table->preparedStmts = TC_TCObjectsAdd(table->preparedStmts, prepStmt, table->heap); - } - } - else - goto free; - break; - } - - case CMD_UPDATE: - { - SQLUpdateStatement* updateStmt = initSQLUpdateStatement(context, driver, parse, true); - SQLBooleanClause* whereClause; - - if (!updateStmt || !litebaseBindUpdateStatement(context, updateStmt)) - goto free; - - if ((whereClause = (updateStmt->whereClause))) - whereClause->expressionTreeBak = cloneTree(whereClause->expressionTree, null, heapParser); - - OBJ_PreparedStatementType(prepStmt) = CMD_UPDATE; - table = updateStmt->rsTable->table; - IF_HEAP_ERROR(table->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto free; - } - setPreparedStatementStatement(prepStmt, updateStmt); - table->preparedStmts = TC_TCObjectsAdd(table->preparedStmts, prepStmt, table->heap); - } - } - } - - if ((i = sqlLength)) // Tokenizes the sql string looking for '?'. - while (--i) - if (sqlChars[i] == '?') - numParams++; - - // juliana@222_8: an array to hook the prepared statement object parameters. - if (!(OBJ_PreparedStatementObjParams(prepStmt) = TC_createArrayObject(context, "[java.lang.Object", numParams))) - goto finish; - TC_setObjectLock(OBJ_PreparedStatementObjParams(prepStmt), UNLOCKED); - - // If the statement is to be used as a prepared statement, it is possible to use log. - if (getPreparedStatementStatement(prepStmt) && logger) - { - int16* paramsPos; - int16* paramsLength; - JCharP* paramsAsStrs; - - if (numParams > 0) - { - // Creates the array of parameters. - setPreparedStatementParamsAsStrs(prepStmt, (paramsAsStrs = (JCharP*)TC_heapAlloc(heapParser, numParams * TSIZE))); - - // Creates the array of the parameters length - setPreparedStatementParamsLength(prepStmt, (paramsLength = (int16*)TC_heapAlloc(heapParser, numParams << 1))); - - i = numParams; - - // juliana@201_15: The prepared statement parameters for logging must be set as "unfilled" when creating it. - while (--i >= 0) - { - if (!(paramsAsStrs[i] = TC_CharP2JCharP("unfilled", 8))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto finish; - } - paramsLength[i] = 8; - } - - OBJ_PreparedStatementStoredParams(prepStmt) = numParams; - } - - // The array of positions of the '?' in the sql. - setPreparedStatementParamsPos(prepStmt, (paramsPos = (int16*)TC_heapAlloc(heapParser, (numParams + 1) << 1))); - - // Marks the positions of the '?'. - paramsPos[numParams] = sqlLength; - while (--sqlLength >= 0) - if (sqlChars[sqlLength] == '?') - paramsPos[--numParams] = sqlLength; - } - if (!TC_htPutPtr(htPS, hashCode, prepStmt)) - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - -finish: ; - - // juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - if (context->thrownException && prepStmt) - freePreparedStatement(0, prepStmt); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/LitebaseConnection public native int getCurrentRowId(String tableName) throws IllegalStateException, NullPointerException; -/** - * Returns the current rowid for a given table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives the current rowid for the table. - */ -LB_API void lLC_getCurrentRowId_s(NMParams p) -{ - TRACE("lLC_getCurrentRowId_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - tableName = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - Table* table; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "getCurrentRowId ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if ((table = getTableFromName(context, driver, tableName))) - p->retI = table->currentRowId; - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/LitebaseConnection public native int getRowCount(String tableName) throws IllegalStateException, NullPointerException; -/** - * Returns the number of valid rows in a table. This may be different from the number of records if a row has been deleted. - * - * @see #getRowCountDeleted(String) - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives the number of valid rows in a table. - */ -LB_API void lLC_getRowCount_s(NMParams p) -{ - TRACE("lLC_getRowCount_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - tableName = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - Table* table; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "getRowCount ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if ((table = getTableFromName(context, driver, tableName))) - p->retI = table->db.rowCount - table->deletedRowsCount; - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/LitebaseConnection public native void setRowInc(String tableName, int inc) throws IllegalArgumentException; -/** - * Sets the row increment used when creating or updating big amounts of data. Using this method greatly increases the speed of bulk insertions - * (about 3x faster). To use it, it is necessary to call it (preferable) with the amount of lines that will be inserted. After the insertion is - * finished, it is NECESSARY to call it again, passing -1 as the increment argument. Without doing this last step, data may be - * lost because some writes will be delayed until the method is called with -1. Another good optimization on bulk insertions is to drop the indexes - * and then create them afterwards. So, to correctly use setRowInc(), it is necessary to: - * - *
    - * driver.setRowInc("table", totalNumberOfRows);
    - * // Fetch the data and insert them.
    - * driver.setRowInc("table", -1);
    - * 
    - * - * Using prepared statements on insertion makes it another a couple of times faster. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->i32[0] The increment value. - * @throws IllegalArgumentException If the increment is equal to 0 or less than -1. - */ -LB_API void lLC_setRowInc_si(NMParams p) -{ - TRACE("lLC_setRowInc_si") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - tableName = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - Table* table; - int32 inc = p->i32[0]; - - if (!inc || inc < -1) - { - TC_throwExceptionNamed(context, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_INC)); - goto finish; - } - - if (logger) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - IntBuf intBuf; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "setRowInc ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName)) - && TC_appendCharP(context, logSBuffer, " ") && TC_appendCharP(context, logSBuffer, TC_int2str(inc, intBuf))) - - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if ((table = getTableFromName(context, driver, tableName))) - { - bool setting = inc != -1; - int32 i = table->columnCount; - PlainDB* plainDB = &table->db; - Index** columnIndexes = table->columnIndexes; - ComposedIndex** composedIndexes = table->composedIndexes; - XFile* dbFile = &plainDB->db; - XFile* dboFile = &plainDB->dbo; - - plainDB->rowInc = setting? inc : DEFAULT_ROW_INC; - while (--i >= 0) // Flushes the simple indices. - if (columnIndexes[i]) - indexSetWriteDelayed(context, columnIndexes[i], setting); - - // juliana@202_18: The composed indices must also be written delayed when setting row increment to a value different to -1. - i = table->numberComposedIndexes; - while (--i >= 0) - indexSetWriteDelayed(context, composedIndexes[i]->index, setting); - - // juliana@227_3: improved table files flush dealing. - if (inc == -1) // juliana@202_17: Flushs the files to disk when setting row increment to -1. - { - dbFile->dontFlush = dboFile->dontFlush = false; - if (dbFile->cacheIsDirty) - flushCache(context, dbFile); // Flushs .db. - if (dboFile->cacheIsDirty) - flushCache(context, dboFile); // Flushs .dbo. - } - else - dbFile->dontFlush = dboFile->dontFlush = true; - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Indicates if the given table already exists. This method can be used before a drop table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives true if a table exists; false othewise. - * @throws DriverException If tableName or path is too big. - */ -LB_API void lLC_exists_s(NMParams p) // litebase/LitebaseConnection public native boolean exists(String tableName) throws DriverException; -{ - TRACE("lLC_exists_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - TCObject driver = p->obj[0], - tableNameObj = p->obj[1]; - char tableNameCharP[DBNAME_SIZE], - bufName[DBNAME_SIZE]; - TCHAR fullName[MAX_PATHNAME]; - - if (String_charsLen(tableNameObj) > MAX_TABLE_NAME_LENGTH) - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - else - { - int32 length = String_charsLen(tableNameObj); - TCHARP sourcePath = getLitebaseSourcePath(driver); - - // juliana@252_3: corrected a possible crash if the path had more than 255 characteres. - if (length + tcslen(sourcePath) + 10 > MAX_PATHNAME) - { - char buffer[1024]; - TC_TCHARP2CharPBuf(sourcePath, buffer); - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_PATH), buffer); - } - else - { - TC_JCharP2CharPBuf(String_charsStart(tableNameObj), length, tableNameCharP); - getDiskTableName(p->currentContext, OBJ_LitebaseAppCrid(driver), tableNameCharP, bufName); - xstrcat(bufName, DB_EXT); - getFullFileName(bufName, sourcePath, fullName); - p->retI = lbfileExists(fullName); - - // juliana@253_10: now a DriverException will be thown if the .db file exists but not .dbo. - length = tcslen(fullName); - fullName[length] = 'o'; - fullName[length + 1] = 0; - if (p->retI && !lbfileExists(fullName)) - { - char path[MAX_PATHNAME]; - TC_TCHARP2CharPBuf(fullName, path); - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_PATH), path); - } - } - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Releases the file handles (on the device) of a Litebase instance. Note that, after this is called, all Resultsets and - * PreparedStatements created with this Litebase instance will be in an inconsistent state, and using them will probably reset the - * device. This method also deletes the active instance for this creator id from Litebase's internal table. - * - * @param p->obj[0] The connection with Litebase. - * @throws IllegalStateException If the driver is closed. - */ -LB_API void lLC_closeAll(NMParams p) // litebase/LitebaseConnection public native void closeAll() throws IllegalStateException; -{ - TRACE("lLC_closeAll") - TCObject driver = p->obj[0]; - Context context = p->currentContext; - - MEMORY_TEST_START - - if (OBJ_LitebaseDontFinalize(driver)) // The driver can't be closed. - TC_throwExceptionNamed(context, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - else - { - TCObject logger = litebaseConnectionClass->objStaticValues[1]; - - if (logger) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "closeAll")) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - -finish: // juliana@214_7: must free Litebase even if the log string creation fails. - freeLitebase(context, (size_t)driver); - xfree(context->litebasePtr); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@201_13: .dbo is now being purged. -// litebase/LitebaseConnection public native int purge(String tableName) throws DriverException, OutOfMemoryError; -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Used to delete physically the records of the given table. Records are always deleted logically, to avoid the need of recreating the indexes. When - * a new record is added, it doesn't uses the position of the previously deleted one. This can make the table big, if a table is created, filled and - * has a couple of records deleted. This method will remove all deleted records and recreate the indexes accordingly. Note that it can take some time - * to run. - *

    - * Important: the rowid of the records is NOT changed with this operation. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The table name to purge. - * @param p->retI Receives the number of purged records. - * @throws DriverException If a row can't be read or written. - * @throws OutOfMemoryError If there is not enough memory to purge the table. - */ -LB_API void lLC_purge_s(NMParams p) -{ - TRACE("lLC_purge_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - tableName = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - Table* table = getTableFromName(context, driver, tableName); - int32 deleted = 0; - - if (logger) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "purge ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - // juliana@270_27: now purge will also really purge the table if it only suffers updates. - if (table && ((deleted = table->deletedRowsCount) > 0 || table->wasUpdated)) // Removes the deleted records from the table. - { - PlainDB* plainDB = &table->db; - XFile* dbFile = &plainDB->db; - Index** columnIndexes = table->columnIndexes; - ComposedIndex** composedIndexes = table->composedIndexes; - int32 willRemain = plainDB->rowCount - deleted, - columnCount = table->columnCount, - i; - - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified - // since its last opening. - if (!setModified(context, table)) - goto finish; - - if (willRemain) - { - // rnovais@570_75: inserts all records at once. - // juliana@223_12: purge now only recreates the .dbo file, reusing .db file on Windows 32, Windows CE, Palm, iPhone, and Android. - char buffer[DBNAME_SIZE]; - XFile newdbo, - olddbo; - uint8* basbuf = plainDB->basbuf; - uint8* columnNulls0 = table->columnNulls; - int8* columnTypes = table->columnTypes; - uint16* columnOffsets = table->columnOffsets; - int32* columnSizes = table->columnSizes; - int32 rowCount = plainDB->rowCount, - id, - j, - - // juliana@230_12: improved recover table to take .dbo data into consideration. - k, - crc32, - dataLength, - length = table->columnOffsets[columnCount] + NUMBEROFBYTES(columnCount), - remain = 0, - type, - numberOfBytes = NUMBEROFBYTES(columnCount); - bool useCrypto = dbFile->useCrypto; - TCHARP sourcePath = getLitebaseSourcePath(driver); - SQLValue* record[MAXIMUMS + 1]; - Heap heap = heapCreate(); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - -free: - heapDestroy(heap); - goto finish; - } - - // Allocates the temporary records. - i = columnCount; - while (--i >= 0) - { - record[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - if (columnTypes[i] == CHARS_TYPE || columnTypes[i] == CHARS_NOCASE_TYPE) - record[i]->asChars = (JCharP)TC_heapAlloc(heap, (columnSizes[i] << 1) + 2); - else if (columnTypes[i] == BLOB_TYPE) - record[i]->asBlob = (uint8*)TC_heapAlloc(heap, columnSizes[i]); - } - - // rnovais@570_61: verifies if it needs to store the currentRowId. - if (plainRead(context, plainDB, rowCount - 1)) - { - xmove4(&id, basbuf); - if ((id & ROW_ATTR_MASK) == ROW_ATTR_DELETED) // Is the last record deleted? - table->auxRowId = table->currentRowId; // rnovais@570_61 - } - else - goto free; - - // Creates the temporary .dbo file. - xstrcpy(buffer, plainDB->dbo.name); - xstrcat(buffer, "_"); - if (!nfCreateFile(context, buffer, true, useCrypto, sourcePath, &newdbo, -1)) // Creates the new .dbo file. - goto free; - - plainDB->rowInc = willRemain; - i = -1; - while (++i < rowCount) - { - if (!readRecord(context, table, record, i, columnNulls0, null, 0, false, null, null)) // juliana@227_20 - goto free; - - xmove4(&record[0]->asInt, plainDB->basbuf); - if ((record[0]->asInt & ROW_ATTR_MASK) != ROW_ATTR_DELETED) // is record ok? - { - xmemmove(&olddbo, &plainDB->dbo, sizeof(XFile)); - xmemmove(&plainDB->dbo, &newdbo, sizeof(XFile)); - - // juliana@225_3: corrected a possible "An attempt was made to move the file pointer before the beginning of the file." on - // some Windows CE devices when doing a purge. - j = -1; - while (++j < columnCount) - if (!writeValue(context, plainDB, record[j], &basbuf[columnOffsets[j]], columnTypes[j], columnSizes[j], true, true, false, - false)) - { - nfRemove(context, &newdbo, sourcePath); - xmemmove(&plainDB->dbo, &olddbo, sizeof(XFile)); - goto free; - } - - xmemmove(&basbuf[columnOffsets[j]], columnNulls0, numberOfBytes); - - // juliana@223_8: corrected a bug on purge that would not copy the crc32 codes for the rows. - // juliana@220_4: added a crc32 code for every record. Please update your tables. - j = basbuf[3]; - basbuf[3] = 0; // juliana@222_5: The crc was not being calculated correctly for updates. - - // juliana@230_12: improved recover table to take .dbo data into consideration. - crc32 = updateCRC32(basbuf, length, 0); - - if (table->version == VERSION_TABLE) - { - k = columnCount; - while (--k >= 0) - if (((type = columnTypes[k]) == CHARS_TYPE || type == CHARS_NOCASE_TYPE) && isBitUnSet(columnNulls0, k)) - crc32 = updateCRC32((uint8*)record[k]->asChars, record[k]->length << 1, crc32); - else if (type == BLOB_TYPE && isBitUnSet(columnNulls0, k)) - { - dataLength = record[k]->length; - crc32 = updateCRC32((uint8*)&dataLength, 4, crc32); - } - } - - xmove4(&basbuf[length], &crc32); // Computes the crc for the record and stores at the end of the record. - basbuf[3] = j; - - if (!plainRewrite(context, plainDB, remain++)) - goto free; - xmemmove(&newdbo, &plainDB->dbo, sizeof(XFile)); - xmemmove(&plainDB->dbo, &olddbo, sizeof(XFile)); - } - } - - if (!nfRemove(context, &olddbo, sourcePath) || !nfRename(context, &newdbo, olddbo.name, sourcePath)) - goto free; - xmemmove(&plainDB->dbo, &newdbo, sizeof(XFile)); - plainDB->rowInc = DEFAULT_ROW_INC; - plainDB->rowCount = remain; - heapDestroy(heap); - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -#if defined(POSIX) || defined(ANDROID) - removeFileFromList(&newdbo); -#endif - - } - else // If no rows will remain, just deletes everyone. - { - XFile* dbo = &plainDB->dbo; - - if ((i = lbfileSetSize(&dbFile->file, 0)) || (i = lbfileSetSize(&dbo->file, 0))) - { - fileError(context, i, dbFile->name); - goto finish; - } - - dbo->finalPos = dbFile->finalPos = dbFile->size = dbo->size = plainDB->rowAvail = plainDB->rowCount = 0; - } - - table->deletedRowsCount = 0; // Empties the deletedRows. - table->wasUpdated = false; // juliana@270_27: now purge will also really purge the table if it only suffers updates. - - // Recreates the simple indices. - i = table->columnCount; - while (--i >= 0) - - // juliana@202_14: Corrected the simple index re-creation when purging the table. - if (columnIndexes[i] && !tableReIndex(context, table, i, false, null)) - goto finish; - - // recreate the composed indexes - if ((i = table->numberComposedIndexes) > 0) - while (--i >= 0) - if (!tableReIndex(context, table, -1, false, composedIndexes[i])) - goto finish; - - // juliana@115_8: saving metadata before recreating the indices does not let .db header become empty. - // Updates the metadata. - plainDB->useOldCrypto = false; - if (!tableSaveMetaData(context, table, TSMD_EVERYTHING)) // guich@560_24 table->saveOnExit = 1; - goto finish; - - // juliana@227_16: purge must truncate the .db file and flush .dbo file in order to ensure that a future recoverTable() won't corrupt the - // table. - if (plainDB->dbo.cacheIsDirty && !flushCache(context, &plainDB->dbo)) // Flushs .dbo. - goto finish; - - // juliana@250_6: corrected a bug on LitebaseConnection.purge() that could corrupt the table. - plainDB->rowAvail = 0; - if ((i = lbfileSetSize(&dbFile->file, dbFile->size = plainDB->rowCount * plainDB->rowSize + plainDB->headerSize)) - || (i = lbfileFlush(dbFile->file))) - { - fileError(context, i, dbFile->name); - goto finish; - } - } - p->retI = deleted; - - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns the number of deleted rows. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives the total number of deleted records of the given table. - */ -LB_API void lLC_getRowCountDeleted_s(NMParams p) // litebase/LitebaseConnection public native int getRowCountDeleted(String tableName); -{ - TRACE("lLC_getRowCountDeleted_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - tableName = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - Table* table; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "getRowCountDeleted ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if ((table = getTableFromName(context, driver, tableName))) - p->retI = table->deletedRowsCount; - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Gets an iterator for a table. With it, it is possible iterate through all the rows of a table in sequence and get its attributes. This is good for - * synchronizing a table. While the iterator is active, it is not possible to do any queries or updates because this can cause dada corruption. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retO receives a iterator for the given table. - */ -LB_API void lLC_getRowIterator_s(NMParams p) // litebase/LitebaseConnection public native litebase.RowIterator getRowIterator(String tableName); -{ - TRACE("lLC_getRowIterator_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - Context context = p->currentContext; - TCObject driver = p->obj[0], - tableName = p->obj[1], - logger = litebaseConnectionClass->objStaticValues[1]; - Table* table = getTableFromName(context, driver, tableName); - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "getRowIterator ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if (table) - { - TCObject rowIterator = p->retO = TC_createObject(context, "litebase.RowIterator"); - - // Creates and populates the row iterator object. - if (rowIterator) - { - TC_setObjectLock(rowIterator, UNLOCKED); - setRowIteratorTable(rowIterator, table); - OBJ_RowIteratorRowNumber(rowIterator) = -1; - OBJ_RowIteratorData(rowIterator) = TC_createArrayObject(context, BYTE_ARRAY, table->db.rowSize); - OBJ_RowIteratorDriver(rowIterator) = driver; - TC_setObjectLock(OBJ_RowIteratorData(rowIterator), UNLOCKED); - } - - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Gets the Litebase logger. The fields should be used unless using the logger within threads. - * - * @param p->retO receives the logger. - */ -LB_API void lLC_privateGetLogger(NMParams p) // litebase/LitebaseConnection public static native totalcross.util.Logger getLogger(); -{ - TRACE("lLC_privateGetLogger") - MEMORY_TEST_START - LOCKVAR(log); - p->retO = litebaseConnectionClass->objStaticValues[1]; - UNLOCKVAR(log); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Sets the litebase logger. This enables log messages for all queries and statements of Litebase and can be very useful to help finding bugs in - * the system. Logs take up memory space, so turn them on only when necessary. The fields should be used unless using the logger within threads. - * - * @param p->obj[0] The logger. - */ -LB_API void lLC_privateSetLogger_l(NMParams p) // litebase/LitebaseConnection public static native void setLogger(totalcross.util.Logger logger); -{ - TRACE("lLC_privateSetLogger_l") - MEMORY_TEST_START - LOCKVAR(log); - litebaseConnectionClass->objStaticValues[1] = p->obj[0]; - UNLOCKVAR(log); - MEMORY_TEST_END -} - -// juliana@230_4: Litebase default logger is now a plain text file instead of a PDB file. -////////////////////////////////////////////////////////////////////////// - // litebase/LitebaseConnection public static native totalcross.util.Logger getDefaultLogger() throws DriverException; -/** - * Gets the default Litebase logger. When this method is called for the first time, a new text file is created. In the subsequent calls, the same - * file is used. - * - * @param p->retI receives the number of files deleted. - * @throws DriverException if an IOException occurs. - */ -LB_API void lLC_privateGetDefaultLogger(NMParams p) -{ - TRACE("lLC_privateGetDefaultLogger") - Context context = p->currentContext; - TCObject nameStr, - logger, - file = null; - TCHAR name[MAX_PATHNAME]; - - MEMORY_TEST_START - LOCKVAR(log); - - // Creates the logger string. - // juliana@225_10: Corrected a possible crash when using the default logger. - if (!(nameStr = TC_createStringObjectFromCharP(context, "litebase", 8)) - || !(p->retO = logger = TC_executeMethod(context, getLogger, nameStr, -1, null).asObj) - || context->thrownException) - goto finish; - - if (!FIELD_I32(FIELD_OBJ(logger, loggerClass, 1), 0)) // Only gets a new default logger if no one exists. - { - LongBuf timeLong; - char strAppId[5]; - int32 year, - month, - day, - hour, - minute, - second, - millis; - - getCurrentPath(name); - tcscat(name, TEXT("/LITEBASE_")); - TC_getDateTime(&year, &month, &day, &hour, &minute, &second, &millis); - TC_CharP2TCHARPBuf(TC_long2str(getTimeLong(year, month, day, hour, minute, second), timeLong), &name[tcslen(name)]); - tcscat(name, TEXT(".")); - TC_int2CRID(TC_getApplicationId(), strAppId); - TC_CharP2TCHARPBuf(strAppId, &name[tcslen(name)]); - tcscat(name, TEXT(".LOGS")); - TC_setObjectLock(nameStr, UNLOCKED); - nameStr = null; - if (!(file = TC_createObject(context, "totalcross.io.File")) - || !(nameStr = TC_createStringObjectFromTCHARP(context, name, -1))) - goto finish; - - FIELD_OBJ(p->obj[0] = file, OBJ_CLASS(file), 0) = p->obj[1] = nameStr; // path - FIELD_I32(file, 1) = p->i32[0] = CREATE_EMPTY; // mode - FIELD_I32(file, 2) = p->i32[1] = -1; // slot - TC_tiF_create_sii(p); - if (context->thrownException) - goto finish; - - TC_executeMethod(context, addOutputHandler, logger, file); - if (context->thrownException) - goto finish; - - } - - FIELD_I32(logger, 0) = 16; - p->retO = logger; - -finish: ; - TC_setObjectLock(file, UNLOCKED); - TC_setObjectLock(nameStr, UNLOCKED); - - // juliana@230_23: now LitebaseConnection.getDefaultLogger() will throw a DriverException instead of an IOException if a file error occurs. - if (context->thrownException && TC_areClassesCompatible(context, OBJ_CLASS(context->thrownException), "totalcross.io.IOException")) - { - TCObject exception = context->thrownException, - exceptionMsg = FIELD_OBJ(exception, OBJ_CLASS(exception), 0); - char msgError[1024]; - - if (exceptionMsg) - { - int32 length = String_charsLen(exceptionMsg); - TC_JCharP2CharPBuf(String_charsStart(exceptionMsg), length < 1024? length : 1023, msgError); - } - else - xstrcpy(msgError, "null"); - - context->thrownException = null; - TC_throwExceptionNamed(context, "litebase.DriverException", msgError); - - if (strEq(OBJ_CLASS(context->thrownException)->name, "litebase.DriverException")) - OBJ_DriverExceptionCause(context->thrownException) = exception; - } - UNLOCKVAR(log); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Deletes all the log files with the default format found in the default device folder. If log is enabled, the current log file is not affected by - * this command. - * - * @param p->retI receives the number of files deleted. - */ -LB_API void lLC_privateDeleteLogFiles(NMParams p) // litebase/LitebaseConnection public static native int deleteLogFiles(); -{ - TRACE("lLC_privateDeleteLogFiles") - Context context = p->currentContext; - TCHARPs* list = null; - TCHAR fullPath[MAX_PATHNAME], - path[MAX_PATHNAME]; - char name[MAX_PATHNAME], - value[DBNAME_SIZE]; - int32 count = 0, - i = 0, - ret; - Heap heap = heapCreate(); - - MEMORY_TEST_START - LOCKVAR(log); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto finish; - } - getCurrentPath(path); - - if ((ret = TC_listFiles(path, 1, &list, &count, heap, 0))) // Lists all the files of the folder. - { - fileError(context, ret, ""); - goto finish; - } - - name[0] = 0; - if (count) - { - TCObject logger = litebaseConnectionClass->objStaticValues[1], - nameObj; - - if (logger) - { - nameObj = ((TCObject*)ARRAYOBJ_START(FIELD_OBJ(FIELD_OBJ(logger, loggerClass, 1), OBJ_CLASS(FIELD_OBJ(logger, loggerClass, 1)), 0)))[0]; - nameObj = FIELD_OBJ(nameObj, OBJ_CLASS(nameObj), 0); - TC_JCharP2CharPBuf(String_charsStart(nameObj), String_charsLen(nameObj), name); - } - } - - while (--count >= 0) - { - TC_TCHARP2CharPBuf(list->value, value); - if (xstrstr(value, "LITEBASE") == value && xstrstr(value, ".LOGS") && !xstrstr(name, value)) // Deletes only the closed log files. - { - getFullFileName(value, path, fullPath); - if ((ret = lbfileDelete(null, fullPath, false))) - { - fileError(context, ret, ""); - goto finish; - } - i++; - } - - list = list->next; - } - - p->retI = i; // The number of log files deleted. - -finish: ; - heapDestroy(heap); - UNLOCKVAR(log); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public static native litebase.LitebaseConnection processLogs(String []sql, String params, boolean isDebug) -// throws DriverException, NullPointerException, OutOfMemoryError; -/** - * This is a handy method that can be used to reproduce all commands of a log file. This is intended to be used by the development team only. - * Here's a sample on how to use it: - * - *

    - * String []sql =
    - * {
    - *    "new LitebaseConnection(MBSL,null)",
    - *    "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20), 
    - *                                DESCRICAO char(100), UNDCAIXA char(10), PESO float, UNIDADEMEDIDA char(3),
    - *                                EMBALAGEM char(10), PORCTROCA float, PERMITETROCA int)",
    - *    "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)",
    - *    "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)",
    - *    "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)",
    - *    "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)",
    - *    "closeAll",
    - *    "new LitebaseConnection(MBSL,null)",
    - *    "insert into PRODUTO values(1,'19132', 2, 1, '1', 2, '3', 'ABSORVENTE SILHO ABAS', '5', 13, 'PCT', '20X30', 10, 0)",
    - *  };
    - *  LitebaseConnection.processLogs(sql, true);
    - * 
    - * - * @param p->obj[0] The string array of SQL commands to be executed. - * @param p->obj[1] The parameters to open a connection. - * @param p->i32[0] Indicates if debug information is to displayed on the debug console. - * @param p->retO Receives the LitebaseConnection instance created, or null if closeAll was the last command executed (or - * no commands were executed at all). - * @throws DriverException If an exception occurs. - * @throws NullPointerException If p->obj[0] is null. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_privateProcessLogs_Ssb(NMParams p) -{ - TRACE("lLC_privateProcessLogs_Ssb") - TCObject driver = null, - sql = p->obj[0], - params = p->obj[1], - string, - resultSetObj; - TCObject* sqlArray = (TCObject*)ARRAYOBJ_START(sql); - Context context = p->currentContext; - bool isDebug = p->i32[0]; - int32 i, - j = -1, - length = ARRAYOBJ_LEN(sql), - sqlLen; - JCharP sqlStr; - ResultSet* resultSet; - - MEMORY_TEST_START - i = j; - - if (sql) - while (++i < length) - { - if (isDebug) - TC_debug("running command # %d", (i + 1)); - string = sqlArray[i]; - - // Gets a new Litebase Connection. - if (JCharPStartsWithCharP(sqlStr = String_charsStart(string), "new LitebaseConnection", sqlLen = String_charsLen(string), 22)) - TC_setObjectLock(p->retO = driver = create(context, getAppCridInt(&sqlStr[23]), params), UNLOCKED); - - // Create command. - else if (JCharPStartsWithCharP(sqlStr, "create", sqlLen, 6)) - litebaseExecute(context, driver, sqlStr, sqlLen); - - // closeAll() command. - else if (JCharPEqualsCharP(sqlStr, "closeAll", sqlLen, 8, true)) - { - freeLitebase(context, (size_t)driver); - p->retO = driver = null; - } - - // Select command. - else if (JCharPStartsWithCharP(sqlStr, "select", sqlLen, 6)) - { - if ((resultSetObj = litebaseExecuteQuery(context, driver, sqlStr, sqlLen))) - { - resultSet = getResultSetBag(resultSetObj); - while (resultSetNext(context, resultSet)); - freeResultSet(resultSet); - TC_setObjectLock(resultSetObj, UNLOCKED); - } - } - - // Commands that update the table. - else if (sqlLen > 0) - litebaseExecuteUpdate(context, driver, sqlStr, sqlLen); - - if (context->thrownException) - { - TCObject exception = context->thrownException, - exceptionMsg = FIELD_OBJ(exception, OBJ_CLASS(exception), 0); - char msgError[1024]; - - if (exceptionMsg) - { - int32 length = String_charsLen(exceptionMsg); - TC_JCharP2CharPBuf(String_charsStart(exceptionMsg), length < 1024? length : 1023, msgError); - } - else - xstrcpy(msgError, "null"); - - if (isDebug) - { - char sqlErr[1024]; - TC_JCharP2CharPBuf(sqlStr, sqlLen < 1024? sqlLen : 1023, sqlErr); - TC_debug("%s - %s", sqlErr, msgError); - } - - context->thrownException = null; - TC_throwExceptionNamed(context, "litebase.DriverException", msgError); - - if (strEq(OBJ_CLASS(context->thrownException)->name, "litebase.DriverException")) - OBJ_DriverExceptionCause(context->thrownException) = exception; - break; - } - else - j++; - } - else - TC_throwNullArgumentException(context, "sql"); - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@220_5: added a method to recover possible corrupted tables, the ones that were not closed properly. -// litebase/LitebaseConnection public native boolean recoverTable(String tableName) throws DriverException, OutOfMemoryError; -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Tries to recover a table not closed properly by marking and erasing logically the records whose crc are not valid. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of the table to be converted. - * @param p->retI Receives the number of purged records. - * @throws DriverException If the table name or path is too big. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_recoverTable_s(NMParams p) -{ - TRACE("lLC_recoverTable_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - TCObject tableName = p->obj[1]; - Context context = p->currentContext; - - if (String_charsLen(tableName) > MAX_TABLE_NAME_LENGTH) - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - else - { - TCObject driver = p->obj[0], - logger = litebaseConnectionClass->objStaticValues[1]; - char name[DBNAME_SIZE]; - TCHARP sourcePath = getLitebaseSourcePath(driver); - TCHAR buffer[MAX_PATHNAME]; - Heap heap = null; - Table* table = null; - PlainDB* plainDB; - uint8* basbuf; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - uint8* columnNulls0; - int32* columnSizes; - Index** columnIndexes; - NATIVE_FILE tableDb; - SQLValue** record; - int32 crid = OBJ_LitebaseAppCrid(driver), - i, - read, - rows, - dataLength, - columnCount, - crcPos, - crc32Lido = 0, - crc32Calc, - deleted = 0, // Invalidates the number of deleted rows. - type; - bool useCrypto = OBJ_LitebaseUseCrypto(driver), - useOldCrypto; - uint32 j; - int32 auxRowId = -1, // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - currentRowId = -1; // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - int8* types; - - // juliana@230_12 -#if defined(POSIX) || defined(ANDROID) - Hashtable* htTables = (Hashtable*)getLitebaseHtTables(driver); -#endif - - if (logger) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "recover table ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if ((j = String_charsLen(tableName)) + tcslen(sourcePath) + 10 > MAX_PATHNAME) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), sourcePath); - goto finish; - } - - // Opens the table file. - TC_JCharP2CharPBuf(String_charsStart(tableName), j, &name[5]); - TC_CharPToLower(&name[5]); // juliana@227_19: corrected a bug in convert() and recoverTable() which could not find the table .db file. - TC_int2CRID(crid, name); - -// juliana@230_12 -#if defined(POSIX) || defined(ANDROID) - if (TC_htGetPtr(htTables, TC_hashCode(&name[5]))) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_OPENED), &name[5]); - goto finish; - } -#endif - - name[4] = '-'; - xstrcat(name, ".db"); - getFullFileName(name, sourcePath, buffer); - - if ((j = lbfileCreate(&tableDb, buffer, READ_WRITE))) // Opens the .db table file. - { - fileError(context, j, name); - goto finish; - } - - // juliana@222_2: the table must be not closed properly in order to recover it. - if ((j = lbfileSetPos(tableDb, 6)) || (j = lbfileReadBytes(tableDb, (CharP)&crc32Lido, 0, 1, &read))) - { - fileError(context, j, name); - lbfileClose(&tableDb); - goto finish; - } - if (read != 1) // juliana@226_8: a table without metadata (with an empty .db, for instance) can't be recovered: it is corrupted. - { - lbfileClose(&tableDb); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), name); - goto finish; - } - - if (useCrypto) - crc32Lido ^= 0xAA; - - if ((crc32Lido & IS_SAVED_CORRECTLY) == IS_SAVED_CORRECTLY) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CLOSED), name); - lbfileClose(&tableDb); - goto finish; - } - lbfileClose(&tableDb); - - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - heapDestroy(heap); - goto finish; - } - - name[xstrlen(name) - 3] = 0; - - // juliana@253_6: the maximum number of keys of a index was duplicated. - // Opens the table even if it was not cloded properly. - if (!(table = tableCreate(context, name, sourcePath, false, (bool)OBJ_LitebaseIsAscii(driver), useCrypto, getLitebaseNodes(driver), - false, heap))) - goto finish; - - i = rows = (plainDB = &table->db)->rowCount; - basbuf = plainDB->basbuf; - columnIndexes = table->columnIndexes; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - record = newSQLValues(columnCount = table->columnCount, heap); - crcPos = (int32)table->columnOffsets[columnCount] + NUMBEROFBYTES(columnCount); - types = table->columnTypes; - columnNulls0 = table->columnNulls; - columnSizes = table->columnSizes; - useOldCrypto = plainDB->useOldCrypto; - - j = columnCount; - while (--j) - if (((type = types[j]) == CHARS_TYPE || type == CHARS_NOCASE_TYPE)) - record[j]->asChars = TC_heapAlloc(heap, columnSizes[j] << 1); - else if (type == BLOB_TYPE) - record[j]->asBlob = TC_heapAlloc(heap, columnSizes[j]); - - while (--i >= 0) // Checks all table records. - { - if (!plainRead(context, plainDB, i)) - goto finish; - - if (isZero(basbuf, crcPos + 4)) // juliana@268_3: Now does not do anything if there are only zeros in a row and removes them. - { - rows--; - continue; - } - - xmove4(&read, basbuf); - if ((read & ROW_ATTR_MASK) == ROW_ATTR_DELETED) // Counts the number of deleted records. - deleted++; - else - { - xmove4(&crc32Lido, &basbuf[crcPos]); - basbuf[3] = 0; // Erases rowid information. - - // juliana@230_12: improved recover table to take .dbo data into consideration. - crc32Calc = updateCRC32(basbuf, crcPos, 0); - - if (table->version == VERSION_TABLE) - { - if (readRecord(context, table, record, i, columnNulls0, null, 0, false, heap, null)) - { - j = columnCount; - while (--j) - if (((type = types[j]) == CHARS_TYPE || type == CHARS_NOCASE_TYPE) && isBitUnSet(columnNulls0, j)) - crc32Calc = updateCRC32((uint8*)record[j]->asChars, record[j]->length << 1, crc32Calc); - else if (type == BLOB_TYPE && isBitUnSet(columnNulls0, j)) - { - dataLength = record[j]->length; - crc32Calc = updateCRC32((uint8*)&dataLength, 4, crc32Calc); - } - } - else - { - context->thrownException = null; - crc32Calc = crc32Lido + 1; - } - } - - if (useOldCrypto) - { - xmove4(&basbuf[crcPos], &crc32Calc); - table->auxRowId = (read & ROW_ID_MASK) + 1; - if (!plainRewrite(context, plainDB, i)) - goto finish; - } - else if (crc32Calc != crc32Lido) // Deletes and invalidates corrupted records. - { - j = ROW_ATTR_DELETED; - xmove4(basbuf, &j); - if (!plainRewrite(context, plainDB, i)) - goto finish; - deleted++; - - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - if (currentRowId < 0) - currentRowId = (read & ROW_ID_MASK) + 1; - } - else // juliana@224_3: corrected a bug that would make Litebase not use the correct rowid after a recoverTable(). - { - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - read = (read & ROW_ID_MASK) + 1; - if (currentRowId < 0) - currentRowId = read; - if (auxRowId < 0) - auxRowId = read; - } - } - } - - table->deletedRowsCount = p->retI = deleted; - plainDB->rowCount = rows; - - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - table->currentRowId = currentRowId; - table->auxRowId = auxRowId; - - // Recreates the indices. - // Simple indices. - while (--columnCount >= 0) - if (columnIndexes[columnCount] && !tableReIndex(context, table, columnCount, false, null)) - goto finish; - - // Recreates the composed indexes. - if ((i = table->numberComposedIndexes)) - { - ComposedIndex** compIndexes = table->composedIndexes; - while (--i >= 0) - if (!tableReIndex(context, table, -1, false, compIndexes[i])) - goto finish; - } - - plainDB->wasNotSavedCorrectly = false; - - // juliana@224_3: corrected a bug that would make Litebase not use the correct rowid after a recoverTable(). - - plainDB->useOldCrypto = false; - - // juliana@270_26: solved a possible duplicate rowid after issuing LitebaseConnection.recoverTable() on a table. - tableSaveMetaData(context, table, TSMD_EVERYTHING); // Saves information concerning deleted rows. - -finish: - if (table) - freeTable(context, table, false, true); // Closes the table. - - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/LitebaseConnection public native void convert(String tableName) throws DriverException, OutOfMemoryError; -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Converts a table from the previous Litebase table version to the current one. If the table format is older than the previous table version, this - * method can't be used. It is possible to know if the table version is not compativel with the current version used in Litebase because an exception - * will be thrown if one tries to open a table with the old format. The table will be closed after using this method. Notice that the table .db file - * will be overwritten. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of the table to be converted. - * @throws DriverException If the table version is not the previous one (too old or the actual used by Litebase) or the table name or path is too big. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_convert_s(NMParams p) -{ - TRACE("lLC_convert_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - TCObject tableName = p->obj[1]; - Context context = p->currentContext; - - if (String_charsLen(tableName) > MAX_TABLE_NAME_LENGTH) - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - else - { - TCObject driver = p->obj[0], - logger = litebaseConnectionClass->objStaticValues[1]; - Heap heap; - char name[DBNAME_SIZE]; - TCHARP sourcePath = getLitebaseSourcePath(driver); - TCHAR buffer[MAX_PATHNAME]; - Table* table = null; - PlainDB* plainDB; - uint8* basbuf; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - uint8* columnNulls0; - XFile dbFile; - NATIVE_FILE tableDb; - SQLValue** record; - int32 crid = OBJ_LitebaseAppCrid(driver), - i, - rowid, - crc32, - length, - rows, - dataLength, - rowSize, - headerSize, - columnCount, - read, - type; - uint32 j = 0; - bool useCrypto = OBJ_LitebaseUseCrypto(driver); - int8* types; - int32* sizes; - -// juliana@230_12 -#if defined(POSIX) || defined(ANDROID) - Hashtable* htTables = (Hashtable*)getLitebaseHtTables(driver); -#endif - - if (logger) // juliana@230_30: reduced log files size. - { - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - LOCKVAR(log); - - // Builds the logger StringBuffer contents. - StringBuffer_count(logSBuffer) = 0; - if (TC_appendCharP(context, logSBuffer, "convert ") - && TC_appendJCharP(context, logSBuffer, String_charsStart(tableName), String_charsLen(tableName))) - TC_executeMethod(context, loggerLogInfo, logger, logSBuffer); // Logs the Litebase operation. - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - if ((i = String_charsLen(tableName)) + tcslen(sourcePath) + 10 > MAX_PATHNAME) - { - char buffer[1024]; - TC_TCHARP2CharPBuf(sourcePath, buffer); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), buffer); - goto finish; - } - - // Opens the .db table file. - TC_JCharP2CharPBuf(String_charsStart(tableName), i, &name[5]); - TC_CharPToLower(&name[5]); // juliana@227_19: corrected a bug in convert() and recoverTable() which could not find the table .db file. - TC_int2CRID(crid, name); - -// juliana@230_12 -#if defined(POSIX) || defined(ANDROID) - if (TC_htGetPtr(htTables, TC_hashCode(&name[5]))) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_OPENED), &name[5]); - goto finish; - } -#endif - - name[4] = '-'; - xstrcat(name, ".db"); - - getFullFileName(name, sourcePath, buffer); - if ((i = lbfileCreate(&tableDb, buffer, READ_WRITE))) // Opens the .db table file. - { - fileError(context, i, name); - goto finish; - } - - // The version must be the previous of the current one. - if ((i = lbfileSetPos(tableDb, 7)) || (i = lbfileReadBytes(tableDb, (CharP)&j, 0, 1, &read))) - { - fileError(context, i, name); - lbfileClose(&tableDb); - goto finish; - } - - if (useCrypto) - j ^= 0xAA; - - if (j != VERSION_TABLE - 1) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_WRONG_PREV_VERSION), name); - lbfileClose(&tableDb); - goto finish; - } - - // Changes the version to be current one and closes it. - j = useCrypto? VERSION_TABLE ^ 0xAA : VERSION_TABLE; - if ((i = lbfileSetPos(tableDb, 7)) || (i = lbfileWriteBytes(tableDb, (CharP)&j, 0, 1, &read))) - { - fileError(context, i, name); - lbfileClose(&tableDb); - goto finish; - } - lbfileClose(&tableDb); - - name[xstrlen(name) - 3] = 0; - - // juliana@225_11: corrected possible memory leaks and crashes when LitebaseConnection.convert() fails. - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - heapDestroy(heap); - goto finish; - } - - // juliana@253_6: the maximum number of keys of a index was duplicated. - // Opens the table even if it was not cloded properly. - if (!(table = tableCreate(context, name, sourcePath, false, (bool)OBJ_LitebaseIsAscii(driver), useCrypto, getLitebaseNodes(driver), - false, heap))) - goto finish; - - dbFile = (plainDB = &table->db)->db; - headerSize = plainDB->headerSize; - basbuf = plainDB->basbuf; - rows = (dbFile.size - headerSize) / (length = (rowSize = plainDB->rowSize) - 4); - plainDB->rowCount = rows; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - record = newSQLValues(columnCount = table->columnCount, heap); - types = table->columnTypes; - sizes = table->columnSizes; - columnNulls0 = table->columnNulls; - - j = columnCount; - while (--j) - if (((type = types[j]) == CHARS_TYPE || type == CHARS_NOCASE_TYPE)) - record[j]->asChars = TC_heapAlloc(heap, sizes[j] << 1); - else if (type == BLOB_TYPE) - record[j]->asBlob = TC_heapAlloc(heap, sizes[j]); - - while (--rows >= 0) // Converts all the records adding a crc code to them. - { - nfSetPos(&dbFile, rows * length + headerSize); - if (!nfReadBytes(context, &dbFile, basbuf, length)) - goto finish; - rowid = basbuf[3]; - basbuf[3] = 0; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - crc32 = updateCRC32(basbuf, length, 0); - - if (table->version == VERSION_TABLE) - { - if (!readRecord(context, table, record, i, columnNulls0, null, 0, false, heap, null)) - goto finish; - j = columnCount; - while (--j) - if (((type = types[j]) == CHARS_TYPE || type == CHARS_NOCASE_TYPE) && isBitUnSet(columnNulls0, j)) - crc32 = updateCRC32((uint8*)record[j]->asChars, record[j]->length << 1, crc32); - else if (type == BLOB_TYPE && isBitUnSet(columnNulls0, j)) - { - dataLength = record[j]->length; - crc32 = updateCRC32((uint8*)&dataLength, 4, crc32); - } - } - - xmove4(&basbuf[length], &crc32); - basbuf[3] = rowid; - nfSetPos(&dbFile, rows * rowSize + headerSize); - nfWriteBytes(context, &dbFile, basbuf, rowSize); - } - -finish: - if (table) - freeTable(context, table, false, false); // Closes the table. - - } - } - - MEMORY_TEST_END -} - -// juliana@223_1: added a method to get the current slot being used. -////////////////////////////////////////////////////////////////////////// -/** - * Used to returned the slot where the tables were stored on Palm OS. Not used anymore. - * - * @param p->retI receives -1. - */ -LB_API void lLC_getSlot(NMParams p) // litebase/LitebaseConnection public native int getSlot(); -{ - TRACE("lLC_getSlot") - p->retI = -1; -} - -// juliana@226_6: added LitebaseConnection.isOpen(), which indicates if a table is open in the current connection. -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Indicates if a table is open or not. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The table name to be checked. - * @param p->retI receives true if the table is open in the current connection; false, otherwise. - * @throws DriverException If the table name is too big. - */ -LB_API void lLC_isOpen_s(NMParams p) // litebase/LitebaseConnection public native boolean isOpen(String tableName) throws DriverException; -{ - TRACE("lLC_isOpen_s") - - MEMORY_TEST_START - - if (checkParamAndDriver(p, "tableName")) // The driver can't be closed and the table name can't be null. - { - TCObject tableName = p->obj[1]; - - if (String_charsLen(tableName) > MAX_TABLE_NAME_LENGTH) - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - else - { - TCObject driver = p->obj[0]; - Hashtable* htTables = getLitebaseHtTables(driver); - int32 length = String_charsLen(tableName); - char nameCharP[DBNAME_SIZE]; - - // Checks if the table name hash code is in the driver hash table. - TC_JCharP2CharPBuf(String_charsStart(tableName), length, nameCharP); - TC_CharPToLower(nameCharP); - p->retI = TC_htGetPtr(htTables, TC_hashCode(nameCharP)) != null; - } - } - - MEMORY_TEST_END -} - -// juliana@226_10: added LitebaseConnection.dropDatabase(). -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public native static void dropDatabase(String crid, String sourcePath, int slot) throws DriverException, -// NullPointerException; -/** - * Drops all the tables from a database represented by its application id and path. - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - * @throws DriverException If the database is not found or a file error occurs. - * @throws NullPointerException If one of the string parameters is null. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_dropDatabase_ssi(NMParams p) -{ - TRACE("lLC_dropDatabase_ssi") - TCObject cridObj = p->obj[0], - pathObj = p->obj[1]; - - MEMORY_TEST_START - if (cridObj) - if (pathObj) - if (String_charsLen(pathObj) >= MAX_PATHNAME - 4 - DBNAME_SIZE) // The path length can't be greater than the buffer size. - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_PATH)); - else - { - TCHARPs* list = null; - char cridStr[5]; - TCHAR fullPath[MAX_PATHNAME], // juliana@230_6 - buffer[MAX_PATHNAME]; - char value[DBNAME_SIZE]; - int32 i, - count = 0; - bool deleted = false; - Heap heap = heapCreate(); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(p->currentContext, "java.lang.OutOfMemoryError", null); - -error: - heapDestroy(heap); - goto finish; - } - - TC_JCharP2CharPBuf(String_charsStart(cridObj), 4, cridStr); - TC_JCharP2TCHARPBuf(String_charsStart(pathObj), String_charsLen(pathObj), fullPath); - - if ((i = TC_listFiles(fullPath, -1, &list, &count, heap, 0))) // Lists all the files of the folder. - { - fileError(p->currentContext, i, ""); - goto error; - } - - while (--count >= 0) // Deletes only the files of the chosen database. - { - TC_TCHARP2CharPBuf(list->value, value); - if (xstrstr(value, cridStr) == value) - { - getFullFileName(value, fullPath, buffer); - if ((i = lbfileDelete(null, buffer, false))) - { - fileError(p->currentContext, i, value); - goto error; - } - deleted = true; - } - - list = list->next; - } - - heapDestroy(heap); - if (!deleted) - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_DB_NOT_FOUND)); - } - else // The string argument can't be null. - TC_throwNullArgumentException(p->currentContext, "sourcePath"); - else // The string argument can't be null. - TC_throwNullArgumentException(p->currentContext, "crid"); - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public native boolean isTableProperlyClosed(String tableName) throws DriverException, NullPointerException; - -/** - * Indicates if a table is closed properly or not. - * - * @param p->obj[1] The table to be verified. - * @param p->retI receives true if the table is closed properly or is open (a not properly closed table can't be opened); - * false, otherwise. - * @throws DriverException If the table is corrupted. - * @throws NullPointerException If tableName is null. - */ -LB_API void lLC_isTableProperlyClosed_s(NMParams p) -{ - TRACE("lLC_isTableProperlyClosed_s") - - MEMORY_TEST_START - - lLC_isOpen_s(p); - - if (!p->currentContext->thrownException && !p->retI) // If the table is open, then it was closed properly. - { - TCObject tableName = p->obj[1]; - Context context = p->currentContext; - - if (String_charsLen(tableName) > MAX_TABLE_NAME_LENGTH) - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - else - { - TCObject driver = p->obj[0]; - -#if defined(POSIX) || defined(ANDROID) - Hashtable* htTables = (Hashtable*)getLitebaseHtTables(driver); -#endif - - char name[DBNAME_SIZE]; - TCHARP sourcePath = getLitebaseSourcePath(driver); - TCHAR buffer[MAX_PATHNAME]; - NATIVE_FILE tableDb; - int32 crid = OBJ_LitebaseAppCrid(driver), - i = 0, - j = 0, - read; - - // Opens the table file. - TC_JCharP2CharPBuf(String_charsStart(tableName), String_charsLen(tableName), &name[5]); - TC_CharPToLower(&name[5]); - TC_int2CRID(crid, name); - -// juliana@230_12 -#if defined(POSIX) || defined(ANDROID) - if (TC_htGetPtr(htTables, TC_hashCode(&name[5]))) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_OPENED), &name[5]); - goto finish; - } -#endif - - name[4] = '-'; - xstrcat(name, ".db"); - getFullFileName(name, sourcePath, buffer); - - if ((j = lbfileCreate(&tableDb, buffer, READ_WRITE))) // Opens the .db table file. - { - fileError(context, j, name); - goto finish; - } - - // Reads the flag. - if ((j = lbfileSetPos(tableDb, 6)) || (j = lbfileReadBytes(tableDb, (CharP)&i, 0, 1, &read))) - { - fileError(context, j, name); - lbfileClose(&tableDb); - goto finish; - } - if (read != 1) // juliana@226_8: a table without metadata (with an empty .db, for instance) is corrupted. - { - fileError(context, j, name); - lbfileClose(&tableDb); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), name); - goto finish; - } - if ((i & IS_SAVED_CORRECTLY) == IS_SAVED_CORRECTLY) - p->retI = true; // The table was closed properly. - else - p->retI = false; // The table was not closed properly. - lbfileClose(&tableDb); - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public native String[] listAllTables() throws DriverException, IllegalStateException, OutOfMemoryError; -/** - * Lists all table names of the current connection. - * - * @param p->retO receives an array of all the table names of the current connection. If the current connection has no tables, an empty list is - * returned. - * @throws DriverException If a file error occurs. - * @throws IllegalStateException If the driver is closed. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_listAllTables(NMParams p) -{ - TRACE("lLC_listAllTables") - TCObject driver = p->obj[0]; - - MEMORY_TEST_START - - if (OBJ_LitebaseDontFinalize(driver)) // The driver can't be closed. - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - else - { - TCHARPs* list = null; - TCObject* array; - Context context = p->currentContext; - char crid[5], - value[DBNAME_SIZE]; - TCHARP path = getLitebaseSourcePath(driver); - int32 i, - j, - count = 0; - Heap heap = heapCreate(); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(p->currentContext, "java.lang.OutOfMemoryError", null); - -error: - heapDestroy(heap); - goto finish; - } - - TC_int2CRID(OBJ_LitebaseAppCrid(driver), crid); - - if ((i = TC_listFiles(path, -1, &list, &count, heap, 0))) // Lists all the files of the folder. - { - fileError(context, i, ""); - goto error; - } - - i = 0; - j = count; - while (--j >= 0) // Deletes only the files of the chosen database. - { - TC_TCHARP2CharPBuf(list->value, value); - - // Selects the .db files that are from the tables of the current connection. - if (xstrstr(value, crid) == value && xstrstr(value, ".db") && !xstrstr(value, ".dbo")) - { - list->value[xstrlen(value) - 3] = 0; - list->value = &list->value[5]; - i++; - } - else - list->value = null; - list = list->next; - } - - if (!(p->retO = TC_createArrayObject(context, "[java.lang.String", i))) - goto finish; - array = (TCObject*)ARRAYOBJ_START(p->retO); - - while (--count >= 0) // Gets only the table names that are from this connection. - { - if (list->value) - { - if (!(*array = TC_createStringObjectFromTCHARP(context, list->value, -1))) - goto error; - TC_setObjectLock(*array++, UNLOCKED); - } - list = list->next; - } - - heapDestroy(heap); - } - - -finish: - TC_setObjectLock(p->retO, UNLOCKED); - - MEMORY_TEST_END -} - -// juliana@253_16: created static methods LitebaseConnection.encryptTables() and decryptTables(). -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public native void encryptTables(String crid, String sourcePath, int slot); -/** - * Encrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - */ -LB_API void lLC_encryptTables_ssi(NMParams p) -{ - TRACE("lLC_encryptTables_ssi") - MEMORY_TEST_START - encDecTables(p, true); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/LitebaseConnection public native void decryptTables(String crid, String sourcePath, int slot); -/** - * Decrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - */ -LB_API void lLC_decryptTables_ssi(NMParams p) -{ - TRACE("lLC_decryptTables_ssi") - MEMORY_TEST_START - encDecTables(p, false); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns the metadata for this result set. - * - * @param p->obj[0] The result set. - * @param p->retO receives the metadata for this result set. - */ -LB_API void lRS_getResultSetMetaData(NMParams p) // litebase/ResultSet public native litebase.ResultSetMetaData getResultSetMetaData(); -{ - TRACE("lRS_getResultSetMetaData") - TCObject resultSet = p->obj[0], - rsMetaData; - - MEMORY_TEST_START - - // The driver and the result set can't be closed. - if (testRSClosed(p->currentContext, resultSet) && (p->retO = rsMetaData = TC_createObject(p->currentContext, "litebase.ResultSetMetaData"))) - { - TC_setObjectLock(rsMetaData, UNLOCKED); - OBJ_ResultSetMetaData_ResultSet(rsMetaData) = resultSet; - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Releases all memory allocated for this object. Its a good idea to call this when you no longer needs it, but it is also called by the GC when the - * object is no longer in use. - * - * @param p->obj[0] The result set. - * @throws IllegalStateException If the result set is closed. - */ -LB_API void lRS_close(NMParams p) // litebase/ResultSet private native void rsClose() throws IllegalStateException; -{ - TRACE("lRS_close") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (OBJ_ResultSetDontFinalize(resultSet)) // The result set can't be closed. - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalStateException", getMessage(ERR_RESULTSET_CLOSED)); - else // juliana@211_4: solved bugs with result set dealing. - { - freeResultSet(getResultSetBag(resultSet)); - OBJ_ResultSetDontFinalize(resultSet) = true; - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Places the cursor before the first record. - * - * @param p->obj[0] The result set. - */ -LB_API void lRS_beforeFirst(NMParams p) // litebase/ResultSet public native void beforeFirst(); -{ - TRACE("lRS_beforeFirst") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - getResultSetBag(resultSet)->pos = -1; - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Places the cursor after the last record. - * - * @param p->obj[0] The result set. - */ -LB_API void lRS_afterLast(NMParams p) // litebase/ResultSet public native void afterLast(); -{ - TRACE("lRS_afterLast") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - rsBag->pos = rsBag->table->db.rowCount; - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Places the cursor in the first record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if it was possible to place the cursor in the first record; false, otherwise. - */ -LB_API void lRS_first(NMParams p) // litebase/ResultSet public native bool first(); -{ - TRACE("lRS_first") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - - rsBag->pos = -1; // Sets the position before the first record. - if (resultSetNext(p->currentContext, rsBag)) // Reads the first record. - p->retI = true; - else - { - rsBag->pos = -1; // guich@_105: sets the record to -1 if it can't read the first position. - p->retI = false; - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Places the cursor in the last record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if it was possible to place the cursor in the last record; false, otherwise. - */ -LB_API void lRS_last(NMParams p) // litebase/ResultSet public native bool last(); -{ - TRACE("lRS_last") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - - rsBag->pos = rsBag->table->db.rowCount; // Sets the position after the last record. - if (resultSetPrev(p->currentContext, rsBag)) // Reads the last record. - p->retI = true; - else - { - rsBag->pos = -1; // guich@_105: sets the record to -1 if it can't read the last position. - p->retI = false; - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Gets the next record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if there is a next record to go to in the result set; false, otherwise. - */ -LB_API void lRS_next(NMParams p) // litebase/ResultSet public native bool next(); -{ - TRACE("lRS_next") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - p->retI = resultSetNext(p->currentContext, getResultSetBag(resultSet)); - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns the previous record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if there is a previous record to go to in the result set; false, otherwise. - */ -LB_API void lRS_prev(NMParams p) // litebase/ResultSet public native bool prev(); -{ - TRACE("lRS_prev") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - p->retI = resultSetPrev(p->currentContext, getResultSetBag(resultSet)); - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a short value that is represented by this column. Note that it is only possible to request this - * column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getShort_i(NMParams p) // litebase/ResultSet public native short getShort(int col) throws DriverException; -{ - TRACE("lRS_getShort_i"); - MEMORY_TEST_START - rsGetByIndex(p, SHORT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a short value that is represented by this column. Note that it is only possible to request this - * column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. This method - * is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getShort_s(NMParams p) // litebase/ResultSet public native short getShort(String colName) throws DriverException; -{ - TRACE("lRS_getShort_s"); - MEMORY_TEST_START - rsGetByName(p, SHORT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns an integer value that is represented by this column. Note that it is only possible to request this - * column as integer if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getInt_i(NMParams p) // litebase/ResultSet public native int getInt(int col) throws DriverException; -{ - TRACE("lRS_getInt_i"); - MEMORY_TEST_START - rsGetByIndex(p, INT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns an integer value that is represented by this column. Note that it is only possible to request this - * column as integer if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getInt_s(NMParams p) // litebase/ResultSet public native int getInt(String colName) throws DriverException; -{ - TRACE("lRS_getInt_s"); - MEMORY_TEST_START - rsGetByName(p, INT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a long value that is represented by this column. Note that it is only possible to request this - * column as long if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retL receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getLong_i(NMParams p) // litebase/ResultSet public native long getLong(int col) throws DriverException; -{ - TRACE("lRS_getLong_i"); - MEMORY_TEST_START - rsGetByIndex(p, LONG_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a long value that is represented by this column. Note that it is only possible to request this - * column as long if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retL receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getLong_s(NMParams p) // litebase/ResultSet public native long getLong(String colName) throws DriverException; -{ - TRACE("lRS_getLong_s"); - MEMORY_TEST_START - rsGetByName(p, LONG_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a float value that is represented by this column. Note that it is only possible to request this - * column as float if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getFloat_i(NMParams p) // litebase/ResultSet public native double getFloat(int col) throws DriverException; -{ - TRACE("lRS_getFloat_i"); - MEMORY_TEST_START - rsGetByIndex(p, FLOAT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a float value that is represented by this column. Note that it is only possible to request this - * column as float if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getFloat_s(NMParams p) // litebase/ResultSet public native double getFloat(String colName) throws DriverException; -{ - TRACE("lRS_getFloat_s"); - MEMORY_TEST_START - rsGetByName(p, FLOAT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a double value that is represented by this column. Note that it is only possible to request this - * column as double if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getDouble_i(NMParams p) // litebase/ResultSet public native double getDouble(int col) throws DriverException; -{ - TRACE("lRS_getDouble_i"); - MEMORY_TEST_START - rsGetByIndex(p, DOUBLE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a double value that is represented by this column. Note that it is only possible to request this - * column as double if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getDouble_s(NMParams p) // litebase/ResultSet public native double getDouble(String colName) throws DriverException; -{ - TRACE("lRS_getDouble_s"); - MEMORY_TEST_START - rsGetByName(p, DOUBLE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a char array that is represented by this column. Note that it is only possible to request this - * column as a char array if it was created as a string. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getChars_i(NMParams p) // litebase/ResultSet public native char[] getChars(int col) throws DriverException; -{ - TRACE("lRS_getChars_i"); - MEMORY_TEST_START - rsGetByIndex(p, CHARS_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a char array that is represented by this column. Note that it is only possible to request this - * column as a char array if it was created as a string. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getChars_s(NMParams p) // litebase/ResultSet public native char[] getChars(String colName) throws DriverException; -{ - TRACE("lRS_getChars_s"); - MEMORY_TEST_START - rsGetByName(p, CHARS_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null - */ -LB_API void lRS_getString_i(NMParams p) // litebase/ResultSet public native String getString(int col) throws DriverException; -{ - TRACE("lRS_getString_i"); - MEMORY_TEST_START - rsGetByIndex(p, UNDEFINED_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. This - * method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null - */ -LB_API void lRS_getString_s(NMParams p) // litebase/ResultSet public native String getString(String colName) throws DriverException; -{ - TRACE("lRS_getString_s"); - MEMORY_TEST_START - rsGetByName(p, -1); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a byte (blob) array that is represented by this column. Note that it is only possible to request - * this column as a blob if it was created this way. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getBlob_i(NMParams p) // litebase/ResultSet public native uint8[] getBlob(int col) throws DriverException; -{ - TRACE("lRS_getBlob_i"); - MEMORY_TEST_START - rsGetByIndex(p, BLOB_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a byte array (blob) that is represented by this column. Note that it is only possible to request - * this column as a blob if it was created this way. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getBlob_s(NMParams p) // litebase/ResultSet public native uint8[] getBlob(String colName) throws DriverException; -{ - TRACE("lRS_getBlob_s"); - MEMORY_TEST_START - rsGetByName(p, BLOB_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Starting from the current cursor position, it reads all result set rows that are being requested. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The number of rows to be fetched, or -1 for all. - * @param p->retO receives a matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column - * elements of the first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is null. - */ -LB_API void lRS_getStrings_i(NMParams p) // litebase/ResultSet public native String[][] getStrings(int count); -{ - TRACE("lRS_getStrings_i") - MEMORY_TEST_START - getStrings(p, p->i32[0]); // juliana@201_2: corrected a bug that would let garbage in the number of records parameter. - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Starting from the current cursor position, it reads all result set rows of the result set. first(), last(), - * prev() or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param p->obj[0] The result set. - * @param p->retO receives a matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column - * elements of the first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is null. - */ -LB_API void lRS_getStrings(NMParams p) // litebase/ResultSet public native String[][] getStrings(); -{ - TRACE("lRS_getStrings") - MEMORY_TEST_START - getStrings(p, -1); // juliana@201_2: corrected a bug that would let garbage in the number of records parameter. - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column index (starting from 1), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE or DATETIME). - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDate_i(NMParams p) // litebase/ResultSet public native totalcross.util.Date getDate(int col); -{ - TRACE("lRS_getDate_i") - MEMORY_TEST_START - rsGetByIndex(p, DATE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Given the column name (case insensitive), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE or DATETIME). This method is slightly slower then the method that accepts a - * column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDate_s(NMParams p) // litebase/ResultSet public native totalcross.util.Date getDate(String colName); -{ - TRACE("lRS_getDate_s") - MEMORY_TEST_START - rsGetByName(p, DATE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Given the column index (starting from 1), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a date if it was created this way. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The colum index. - * @param p->retO receives the time of the DATETIME. If the DATETIME value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDateTime_i(NMParams p) // litebase/ResultSet public native totalcross.sys.Time getDateTime(int colIdx); -{ - TRACE("lRS_getDateTime_i") - MEMORY_TEST_START - rsGetByIndex(p, DATETIME_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Given the column name (case insensitive), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a date if it was created this way. This method is slightly slower then the - * method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[0] The colum name. - * @param p->retO receives the time of the DATETIME. If the DATETIME value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDateTime_s(NMParams p) // litebase/ResultSet public native totalcross.sys.Time getDateTime(String colName); -{ - TRACE("lRS_getDateTime_s") - MEMORY_TEST_START - rsGetByName(p, DATETIME_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Places this result set cursor at the given absolute row. This is the absolute physical row of the table. This method is usually used to restore - * the row at a previous row got with the getRow() method. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The row to set the cursor. - */ -LB_API void lRS_absolute_i(NMParams p) // litebase/ResultSet public native bool absolute(int row); -{ - TRACE("lRS_absolute_i") - Context context = p->currentContext; - TCObject resultSet = p->obj[0]; - int32 row = p->i32[0], - i = 0; - - MEMORY_TEST_START - - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(p->obj[0]); - Table* table = rsBag->table; - PlainDB* plainDB = &table->db; - uint8* rowsBitmap = rsBag->allRowsBitmap; - int32 rowCountLess1 = plainDB->rowCount - 1; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap != null) - { - while (i <= rowCountLess1 && i <= row) - { - if (isBitUnSet(rowsBitmap, i)) - row++; - i++; - } - - if ((p->retI = plainRead(context, plainDB, rsBag->pos = i - 1))) - xmemmove(table->columnNulls, plainDB->basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - else - goto finish; - } - else if (table->deletedRowsCount > 0) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - int32 rowCount = 0; - - // Continues searching the position until finding the right row or the end of the result set table. - while (rowCount <= rowCountLess1 && rowCount <= row) - { - // Reads the next row. - rsBag->pos = rowCount; - if (!plainRead(context, plainDB, rowCount++)) - goto finish; - xmove4(&i, plainDB->basbuf); - - if ((i & ROW_ATTR_MASK) == ROW_ATTR_DELETED) // If it was deleted, one more row will be read in total. - row++; - } - xmemmove(table->columnNulls, plainDB->basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - else if (0 <= row && row <= rowCountLess1) - { - rsBag->pos = row; - if ((p->retI = plainRead(context, plainDB, row))) - xmemmove(table->columnNulls, plainDB->basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Moves the cursor rows in distance. The value can be greater or lower than zero. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The distance to move the cursor. - * @param p->retI receives true whenever this method does not throw an exception. - */ -LB_API void lRS_relative_i(NMParams p) // litebase/ResultSet public native bool relative(int rows); -{ - TRACE("lRS_relative_i") - TCObject resultSet = p->obj[0]; - Context context = p->currentContext; - - MEMORY_TEST_START - - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - Table* table = rsBag->table; - PlainDB* plainDB = &table->db; - uint8* rowsBitmap = rsBag->allRowsBitmap; - int32 rows = p->i32[0], - rowCountLess1 = plainDB->rowCount - 1, - pos = rsBag->pos; - - if (rowsBitmap) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - // Continues searching the position until finding the right row or the end or the beginning of the result set table. - if (rows > 0) - while (--rows >= 0) - while (pos++ < rowCountLess1 && isBitUnSet(rowsBitmap, pos)); - else - while (++rows <= 0) - while (pos-- > 0 && isBitUnSet(rowsBitmap, pos)); - - if (pos < 0) - while (pos++ < rowCountLess1 && isBitUnSet(rowsBitmap, pos)); - if (pos > plainDB->rowCount - 1) - while (pos-- > 0 && isBitUnSet(rowsBitmap, pos)); - - if (plainRead(context, plainDB, rsBag->pos = pos)) - xmemmove(table->columnNulls, plainDB->basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - else if (table->deletedRowsCount) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - int32 value; - uint8* basbuf = plainDB->basbuf; - - // Continues searching the position until finding the right row or the end or the beginning of the result set table. - if (rows > 0) - while (--rows >= 0) - while (pos++ < rowCountLess1) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - if (!plainRead(context, plainDB, pos)) - goto finish; - xmove4(&value, basbuf); - if ((value & ROW_ATTR_MASK) != ROW_ATTR_DELETED) - break; - } - else - while (++rows <= 0) - while (pos-- > 0) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - if (!plainRead(context, plainDB, pos)) - goto finish; - xmove4(&value, basbuf); - if ((value & ROW_ATTR_MASK) != ROW_ATTR_DELETED) - break; - } - if (pos < 0) - while (pos++ < rowCountLess1) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - if (!plainRead(context, plainDB, pos)) - goto finish; - xmove4(&value, basbuf); - if ((value & ROW_ATTR_MASK) != ROW_ATTR_DELETED) - break; - } - if (pos > plainDB->rowCount - 1) - while (pos-- > 0) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - if (!plainRead(context, plainDB, pos)) - goto finish; - xmove4(&value, basbuf); - if ((value & ROW_ATTR_MASK) != ROW_ATTR_DELETED) - break; - } - - rsBag->pos = pos; - xmemmove(table->columnNulls, plainDB->basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - else - { - // The new pos is pos + rows or 0 (if pos + rows < 0) or bag.lastRecordIndex (if pos + rows > bag.lastRecordIndex). - int32 newPos = MAX(0, MIN(plainDB->rowCount - 1, rsBag->pos + rows)); - if (rsBag->pos != newPos) // If there are no deleted rows, just reads the row in the right position. - { - rsBag->pos = newPos; - if ((p->retI = plainRead(context, plainDB, newPos))) - xmemmove(table->columnNulls, plainDB->basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - } - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@265_1: corrected getRow() behavior, which must match with absolute(). -/** - * Returns the current physical row of the table where the cursor is. It must be used with absolute() method. - * - * @param p->obj[0] The result set. - * @param p->retI receives the current physical row of the table where the cursor is. - */ -LB_API void lRS_getRow(NMParams p) // litebase/ResultSet public native int getRow(); -{ - TRACE("lRS_getRow") - TCObject resultSet = p->obj[0]; - Context context = p->currentContext; - - MEMORY_TEST_START - - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* resultSetBag = getResultSetBag(resultSet); - Table* table = resultSetBag->table; - PlainDB* plainDB = &table->db; - uint8* rowsBitmap = resultSetBag->allRowsBitmap; - uint8* basbuf = plainDB->basbuf; - int32 pos = resultSetBag->pos; - - if (pos == -1 || pos == plainDB->rowCount) - p->retI = pos; - else if (rowsBitmap) - { - int32 i = -1, - absolute = 0; - - while (++i < pos) - if (isBitSet(rowsBitmap, i)) - absolute++; - p->retI = absolute; - } - else if (table->deletedRowsCount) - { - int32 i = -1, - absolute = 0, - value; - - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (++i < pos) - { - if (!plainRead(context, plainDB, i)) - goto finish; - xmove4(&value, basbuf); - if ((value & ROW_ATTR_MASK) != ROW_ATTR_DELETED) - absolute++; - } - - if (plainRead(context, plainDB, i - 1)) - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - p->retI = absolute; - } - else - p->retI = pos; // Returns the current position of the cursor. - } -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Sets the number of decimal places that the given column (starting from 1) will have when being converted to String. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column. - * @param p->i32[1] The number of decimal places. - * @throws DriverException If the column index is invalid, or the value for decimal places is invalid. - */ -LB_API void lRS_setDecimalPlaces_ii(NMParams p) // litebase/ResultSet public native void setDecimalPlaces(int col, int places) throws DriverException; -{ - TRACE("lRS_setDecimalPlaces_ii") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - int32 column = p->i32[0] - 1, - places = p->i32[1]; - - if (column < 0 || column >= rsBag->selectClause->fieldsCount) // The columns given by the user ranges from 1 to n. - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INVALID_COLUMN_NUMBER), column); - else - { - int32 type; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rsBag->allRowsBitmap || rsBag->isSimpleSelect) - { - SQLResultSetField* field = rsBag->selectClause->fieldList[column]; - column = field->parameter? field->parameter->tableColIndex : field->tableColIndex; - } - - type = rsBag->table->columnTypes[column]; // Gets the column type. - - if (places < -1 || places > 40) // Invalid value for decimal places. - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_RS_DEC_PLACES_START), places); - else if (type == FLOAT_TYPE || type == DOUBLE_TYPE) // Only sets the decimal places if the type is FLOAT or DOUBLE. - rsBag->decimalPlaces[column] = places; - else - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES)); - } - } - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns the number of rows of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI receives the number of rows. - */ -LB_API void lRS_getRowCount(NMParams p) // litebase/ResultSet public native int getRowCount(); -{ - TRACE("lRS_getRowCount") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@114_10: removes the deleted rows. - p->retI = rsBag->allRowsBitmap? rsBag->answerCount : rsBag->table->db.rowCount - rsBag->table->deletedRowsCount; - } - - MEMORY_TEST_END -} -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Given the column index (starting from 1), indicates if this column has a NULL. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives true if the value is SQL NULL; false, otherwise. - */ -LB_API void lRS_isNull_i(NMParams p) // litebase/ResultSet public native boolean isNull(int col); -{ - TRACE("lRS_isNull_i") - TCObject resultSet = p->obj[0]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - rsPrivateIsNull(p); - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Given the column name (case insensitive), indicates if this column has a NULL. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retI receives true if the value is SQL NULL; false, otherwise. - * @throws NullPointerException If the column name is null. - */ -LB_API void lRS_isNull_s(NMParams p) // litebase/ResultSet public native boolean isNull(String colName) throws NullPointerException; -{ - TRACE("lRS_isNull_s") - TCObject resultSet = p->obj[0], - colName = p->obj[1]; - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - if (colName) - { - p->i32[0] = TC_htGet32Inv(&getResultSetBag(resultSet)->intHashtable, identHashCode(colName)) + 1; - rsPrivateIsNull(p); - } - else - TC_throwNullArgumentException(p->currentContext, "colName"); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@270_30: added ResultSet.rowToString(). -/** - * Transforms a ResultSet row in a string. - * - * @param p->obj[0] The result set. - * @param p->retO receives a whole current row of a ResultSet in a string with column data separated by tab. - */ -LB_API void lRS_rowToString(NMParams p) -{ - TRACE("lRS_rowToString") - TCObject resultSetObj = p->obj[0]; - Context context = p->currentContext; - - MEMORY_TEST_START - - if (testRSClosed(context, resultSetObj)) // The driver and the result set can't be closed. - { - ResultSet* resultSet = getResultSetBag(resultSetObj); - Table* table = resultSet->table; - int32 position = resultSet->pos; - - if (position >= 0 && position <= table->db.rowCount - 1) // Invalid result set position. - { - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - int8* columnTypes = table->columnTypes; - int8* decimalPlaces = resultSet->decimalPlaces; - uint8* columnNulls0 = table->columnNulls; - SQLValue value; - bool notTemporary = resultSet->answerCount >= 0 || resultSet->isSimpleSelect; - SQLResultSetField** fields = resultSet->selectClause->fieldList; - SQLResultSetField* field; - TCObject strings[MAXIMUMS]; - TCObject result; - JCharP resultStr; - - // juliana@211_4: solved bugs with result set dealing. - // juliana@211_3: the string matrix size can't take into consideration rows that are before the result set pointer. - int32 cols = resultSet->selectClause->fieldsCount, - i = cols, - j = 0, - k, - finalSize = 0; - - while (--i >= 0) // Fetches all the strings. - { - field = fields[i]; - k = notTemporary? (field->parameter? field->parameter->tableColIndex : field->tableColIndex) : i; - - if (isBitUnSet(columnNulls0, k) && columnTypes[k] != BLOB_TYPE) - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - if (!(strings[i] = rsGetString(context, resultSet, k, &value)) || field->isDataTypeFunction) - { - if (field->isDataTypeFunction) - { - rsApplyDataTypeFunction(p, &value, field, UNDEFINED_TYPE); - if (columnTypes[k] == CHARS_TYPE || columnTypes[k] == CHARS_NOCASE_TYPE) - { - if ((strings[i] = TC_createStringObjectWithLen(context, value.length))) - xmemmove(String_charsStart(strings[i]), value.asChars, value.length << 1); - } - else - TC_setObjectLock(strings[i] = p->retO, LOCKED); - } - else - { - createString(p, &value, columnTypes[k], decimalPlaces? decimalPlaces[k] : -1); - TC_setObjectLock(strings[i] = p->retO, LOCKED); - } - - } - } - else - strings[i] = null; - } - - // Fetches the final size of the resultant string - i = cols; - while (--i >= 0) - if (strings[i]) - finalSize += String_charsLen(strings[i]); - finalSize += cols - 1; - - TC_setObjectLock(p->retO = result = TC_createStringObjectWithLen(context, finalSize), UNLOCKED); - resultStr = String_charsStart(result); - - // Copies the strings to the resultant string. - i = -1; - while (++i < cols) - { - if (strings[i]) - xmemmove(&resultStr[j], String_charsStart(strings[i]), (k = String_charsLen(strings[i])) << 1); - else - k = 0; - if (i + 1 < cols) - resultStr[j += k] = '\t'; - j++; - TC_setObjectLock(strings[i], UNLOCKED); - } - } - else - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_RS_INV_POS), position); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Gets the number of columns for this ResultSet. - * - * @param p->obj[0] The result set meta data. - * @param p->retI receives the number of columns for this ResultSet. - */ -LB_API void lRSMD_getColumnCount(NMParams p) // litebase/ResultSetMetaData public native int getColumnCount(); -{ - TRACE("lRSMD_getColumnCount") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not - // in the select clause. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@210_1: select * from table_name does not create a temporary table anymore. - p->retI = getResultSetBag(resultSet)->selectClause->fieldsCount; - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. -// litebase/ResultSetMetaData public native int getColumnDisplaySize(int column) throws IllegalArgumentException; -/** - * Given the column index (starting at 1), returns the display size. For chars, it will return the number of chars defined; for primitive types, it - * will return the number of decimal places it needs to be displayed correctly. Returns 0 if an error occurs. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retI receives the display size or -1 if a problem occurs. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnDisplaySize_i(NMParams p) -{ - TRACE("lRSMD_getColumnDisplaySize_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - int32 column = p->i32[0] - 1; - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not - // in the select clause. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@213_5: Now a DriverException is thrown instead of returning an invalid value. - if (column < 0 || column >= rsBag->selectClause->fieldsCount) - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER)); - else - { - if (rsBag->allRowsBitmap || rsBag->isSimpleSelect) - { - SQLResultSetField* field = rsBag->selectClause->fieldList[column]; - column = field->parameter? field->parameter->tableColIndex : field->tableColIndex; - } - - // juliana@210_1: select * from table_name does not create a temporary table anymore. - - switch (rsBag->table->columnTypes[column]) - { - case SHORT_TYPE: - p->retI = 6; - break; - case INT_TYPE: - p->retI = 11; - break; - case LONG_TYPE: - p->retI = 20; - break; - case FLOAT_TYPE: - p->retI = 13; - break; - case DOUBLE_TYPE: - p->retI = 21; - break; - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - p->retI = rsBag->table->columnSizes[column]; - break; - case DATE_TYPE: // rnovais@570_12 - p->retI = 11; - break; - case DATETIME_TYPE: // rnovais@570_12 - p->retI = 31; // (10 + 19) - break; - case BLOB_TYPE: - p->retI = -1; - } - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. -// litebase/ResultSetMetaData public native String getColumnLabel(int column) throws IllegalArgumentException; -/** - * Given the column index (starting at 1), returns the column name. Note that if an alias is used to the column, the alias will be returned instead. - * If an error occurs, an empty string is returned. Note that LitebaseConnection 2.x tables must be recreated to be able to return this label - * information. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retO receives the name or alias of the column, which can be an empty string if an error occurs. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnLabel_i(NMParams p) -{ - TRACE("lRSMD_getColumnLabel_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - int32 column = p->i32[0]; - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not - // in the select clause. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@213_5: Now a DriverException is thrown instead of returning an invalid value. - if (column <= 0 || column > rsBag->selectClause->fieldsCount) - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER)); - else // juliana@210_1: select * from table_name does not create a temporary table anymore. - TC_setObjectLock(p->retO = TC_createStringObjectFromCharP(p->currentContext, rsBag->selectClause->fieldList[column - 1]->alias, -1), - UNLOCKED); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. -// litebase/ResultSetMetaData public native int getColumnType(int column) throws IllegalArgumentException; -/** - * Given the column index (starting at 1), returns the column type. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retI receives the column type, which can be: SHORT_TYPE, INT_TYPE, - * LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE, CHAR_TYPE, - * CHAR_NOCASE_TYPE, DATE_TYPE, DATETIME_TYPE, or BLOB_TYPE. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnType_i(NMParams p) -{ - TRACE("lRSMD_getColumnType_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - int32 column = p->i32[0] - 1; - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not - // in the select clause. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@213_5: Now a DriverException is thrown instead of returning an invalid value. - if (column < 0 || column >= rsBag->selectClause->fieldsCount) - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER)); - else - p->retI = rsBag->selectClause->fieldList[column]->dataType; - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/ResultSetMetaData public native String getColumnTypeName(int column) throws DriverException; -/** - * Given the column index (starting at 1), returns the name of the column type. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retO receives the name of the column type, which can be: chars, short, int, - * long, float, double, date, datetime, - * blob, or null if an error occurs. - */ -LB_API void lRSMD_getColumnTypeName_i(NMParams p) -{ - TRACE("lRSMD_getColumnTypeName_i") - CharP ret = ""; - - lRSMD_getColumnType_i(p); - MEMORY_TEST_START - - switch (p->retI) - { - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - ret = "chars"; - break; - case SHORT_TYPE: - ret = "short"; - break; - case INT_TYPE: - ret = "int"; - break; - case LONG_TYPE: - ret = "long"; - break; - case FLOAT_TYPE: - ret = "float"; - break; - case DOUBLE_TYPE: - ret = "double"; - break; - case DATE_TYPE: - ret = "date"; - break; - case DATETIME_TYPE: - ret = "datetime"; - break; - case BLOB_TYPE: - ret = "blob"; - } - - TC_setObjectLock(p->retO = TC_createStringObjectFromCharP(p->currentContext, ret, -1), UNLOCKED); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/ResultSetMetaData public native String getColumnTableName(int columnIdx) throws IllegalArgumentException; -/** - * Given the column index, (starting at 1) returns the name of the table it came from. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index. - * @param p->retO receives the name of the table it came from or null if the column index does not exist. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnTableName_i(NMParams p) -{ - TRACE("lRSMD_getColumnTableName_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - SQLResultSetField** fields = rsBag->selectClause->fieldList; - int32 column = p->i32[0] - 1; - - p->retO = null; - - // juliana@230_36: corrected ResultSetMetaData returning extra columns in queries with order by where there are ordered fields that are not - // in the select clause. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@213_5: Now a DriverException is thrown instead of returning an invalid value. - if (column < 0 || column >= rsBag->selectClause->fieldsCount) - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER)); - else - - // null is a valid return value. - TC_setObjectLock(p->retO = fields[column]->tableName? TC_createStringObjectFromCharP(p->currentContext, fields[column]->tableName, -1) - : null, UNLOCKED); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/ResultSetMetaData public native String getColumnTableName(String columnName) throws DriverException, NullPointerException; -/** - * Given the column name or alias, returns the name of the table it came from. - * - * @param p->obj[0] The result set meta data. - * @param p->obj[1] The column name. - * @param p->retO receives the name of the table it came from or null if the column name does not exist. - * @throws DriverException If the column was not found. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_getColumnTableName_s(NMParams p) -{ - TRACE("lRSMD_getColumnTableName_s") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - - MEMORY_TEST_START - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - TCObject columnNameStr = p->obj[1]; - - if (columnNameStr) - { - SQLSelectClause* clause = getResultSetBag(resultSet)->selectClause; - SQLResultSetField** fields = clause->fieldList; - int32 i = -1, - length = clause->fieldsCount; - JCharP columnNameJCharP = String_charsStart(columnNameStr); - int32 columnNameLength = String_charsLen(columnNameStr); - CharP tableColName, - tableName; - - p->retO = null; - - while (++i < length) // Gets the name of the table or its alias given the column name. - { - if ((((tableColName = fields[i]->tableColName) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - || ((tableColName = fields[i]->alias) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - { - TC_setObjectLock(p->retO = (tableName = fields[i]->tableName)? TC_createStringObjectFromCharP(p->currentContext, tableName, -1) - : null, UNLOCKED); - break; - } - } - if (i == length) // Column name or alias not found. - { - tableColName = TC_JCharP2CharP(columnNameJCharP, columnNameLength); - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), tableColName? tableColName : ""); - xfree(tableColName); - } - } - else // The column name can't be null. - TC_throwNullArgumentException(p->currentContext, "columnName"); - } - - MEMORY_TEST_END -} - -// juliana@227_2: added methods to indicate if a column of a result set is not null or has default values. -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@227_2: added methods to indicate if a column of a result set is not null or has default values. -// litebase/ResultSetMetaData public native boolean hasDefaultValue(int columnIndex) throws DriverException; -/** - * Indicates if a column of the result set has default value. - * - * @param p->i32[0] The column index. - * @param p->retI receives true if the column has a default value; false, otherwise. - * @throws DriverException If the column does not have an underlining table. - */ -LB_API void lRSMD_hasDefaultValue_i(NMParams p) -{ - TRACE("lRSMD_hasDefaultValue_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]), - nameObj = null; - - MEMORY_TEST_START - - lRSMD_getColumnTableName_i(p); // It already tests if the result set is valid. - - if (!p->currentContext->thrownException && (nameObj = p->retO)) - { - ResultSet* rsBag = getResultSetBag(resultSet); - Table* table; - char nameCharP[DBNAME_SIZE]; - - // Gets the table column info. - TC_JCharP2CharPBuf(String_charsStart(nameObj), String_charsLen(nameObj), nameCharP); - if ((table = getTable(p->currentContext, rsBag->driver, nameCharP))) - { - SQLResultSetField* field = rsBag->selectClause->fieldList[p->i32[0] - 1]; - - // juliana@252_6: corrected a possible bug when using ResultSetMetaData in tables with more than 128 columns. - p->retI = (table->columnAttrs[field->parameter? field->parameter->tableColIndex : field->tableColIndex] - & ATTR_COLUMN_HAS_DEFAULT) != 0; - } - } - else if (!nameObj) // The column does not have an underlining table. - { - IntBuf buffer; - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), TC_int2str(p->i32[0], buffer)); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/ResultSetMetaData public native boolean hasDefaultValue(String columnName) throws DriverException, NullPointerException; -/** - * Indicates if a column of the result set has default value. - * - * @param p->obj[1] The column name. - * @param p->retI receives true if the column has a default value; false, otherwise. - * @throws DriverException If the column was not found or does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_hasDefaultValue_s(NMParams p) -{ - TRACE("lRSMD_hasDefaultValue_s") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - Context context = p->currentContext; - - MEMORY_TEST_START - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - TCObject columnNameStr = p->obj[1]; - - if (columnNameStr) - { - SQLResultSetField** fields = rsBag->selectClause->fieldList; - SQLResultSetField* field; - JCharP columnNameJCharP = String_charsStart(columnNameStr); - int32 i = -1, - length = rsBag->selectClause->fieldsCount, - columnNameLength = String_charsLen(columnNameStr); - CharP tableColName; - - p->retO = null; - - while (++i < length) // Gets the name of the table or its alias given the column name. - { - if ((((tableColName = (field = fields[i])->tableColName) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - || ((tableColName = fields[i]->alias) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - { - if (field->tableName) - { - Table* table; - if ((table = getTable(context, rsBag->driver, field->tableName))) - - // juliana@252_6: corrected a possible bug when using ResultSetMetaData in tables with more than 128 columns. - p->retI = (table->columnAttrs[field->parameter? field->parameter->tableColIndex : field->tableColIndex] - & ATTR_COLUMN_HAS_DEFAULT) != 0; - } - else - i = length; - break; - } - } - if (i == length) // Column name or alias not found. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), - (tableColName = TC_JCharP2CharP(columnNameJCharP, columnNameLength))? tableColName : ""); - xfree(tableColName); - } - } - else // The column name can't be null. - TC_throwNullArgumentException(context, "columnName"); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Indicates if a column of the result set is not null. - * - * @param p->i32[0] The column index. - * @param p->retI receives true if the column is not null; false, otherwise. - * @throws DriverException If the column does not have an underlining table. - */ -LB_API void lRSMD_isNotNull_i(NMParams p) // litebase/ResultSetMetaData public native boolean isNotNull(int columnIndex) throws DriverException; -{ - TRACE("lRSMD_isNotNull_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]), - nameObj = null; - Context context = p->currentContext; - - MEMORY_TEST_START - - lRSMD_getColumnTableName_i(p); // It already tests if the result set is valid. - - if (!context->thrownException && (nameObj = p->retO)) - { - ResultSet* rsBag = getResultSetBag(resultSet); - Table* table; - char nameCharP[DBNAME_SIZE]; - - // Gets the table column info. - TC_JCharP2CharPBuf(String_charsStart(nameObj), String_charsLen(nameObj), nameCharP); - if ((table = getTable(context, rsBag->driver, nameCharP))) - { - SQLResultSetField* field = rsBag->selectClause->fieldList[p->i32[0] - 1]; - - // juliana@252_6: corrected a possible bug when using ResultSetMetaData in tables with more than 128 columns. - p->retI = (table->columnAttrs[field->parameter? field->parameter->tableColIndex : field->tableColIndex] - & ATTR_COLUMN_IS_NOT_NULL) != 0; - } - } - else if (!nameObj) // The column does not have an underlining table. - { - IntBuf buffer; - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), TC_int2str(p->i32[0], buffer)); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/ResultSetMetaData public native boolean isNotNull(String columnName) throws DriverException, NullPointerException; -/** - * Indicates if a column of the result set is not null. - * - * @param p->obj[1] The column name. - * @param p->retI receives true if the column is not null; false, otherwise. - * @throws DriverException If the column was not found or does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_isNotNull_s(NMParams p) -{ - TRACE("lRSMD_isNotNull_s") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - Context context = p->currentContext; - - MEMORY_TEST_START - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - TCObject columnNameStr = p->obj[1]; - - if (columnNameStr) - { - SQLResultSetField** fields = rsBag->selectClause->fieldList; - SQLResultSetField* field; - JCharP columnNameJCharP = String_charsStart(columnNameStr); - int32 i = -1, - length = rsBag->selectClause->fieldsCount, - columnNameLength = String_charsLen(columnNameStr); - CharP tableColName; - - p->retO = null; - - while (++i < length) // Gets the name of the table or its alias given the column name. - { - if ((((tableColName = (field = fields[i])->tableColName) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - || ((tableColName = fields[i]->alias) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - { - if (field->tableName) - { - Table* table; - if ((table = getTable(context, rsBag->driver, field->tableName))) - - // juliana@252_6: corrected a possible bug when using ResultSetMetaData in tables with more than 128 columns. - p->retI = (table->columnAttrs[field->parameter? field->parameter->tableColIndex : field->tableColIndex] - & ATTR_COLUMN_IS_NOT_NULL) != 0; - } - else - i = length; - break; - } - } - if (i == length) // Column name or alias not found. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), - (tableColName = TC_JCharP2CharP(columnNameJCharP, columnNameLength))? tableColName : ""); - xfree(tableColName); - } - } - else // The column name can't be null. - TC_throwNullArgumentException(context, "columnName"); - } - - MEMORY_TEST_END -} - -// juliana@253_3: added methods to return the primary key columns of a table. -////////////////////////////////////////////////////////////////////////// -// litebase/ResultSetMetaData public native byte[] getPKColumnIndices(String tableName) throws NullPointerException; -/** - * Returns the primary key column indices of a table. - * - * @param p->obj[1] The table name. - * @param p->retO receives null if the given table does not have primary key or an array with the column indices of the primary key. - * @throws NullPointerException if the table name is null. - */ -LB_API void lRSMD_getPKColumnIndices_s(NMParams p) -{ - TRACE("lRSMD_getPKColumnIndices_s") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - Context context = p->currentContext; - - MEMORY_TEST_START - - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - TCObject tableNameStr = p->obj[1]; - - if (tableNameStr) - { - ResultSet* rsBag = getResultSetBag(resultSet); - char tableNameCharP[DBNAME_SIZE]; - Table* table; - - // Gets the table given its name in the result set. - TC_JCharP2CharPBuf(String_charsStart(tableNameStr), String_charsLen(tableNameStr), tableNameCharP); - TC_CharPToLower(tableNameCharP); - if (!(table = getTableRS(context, rsBag, tableNameCharP))) - goto finish; - - if (table->primaryKeyCol != NO_PRIMARY_KEY) // Simple primary key. - { - if (!(p->retO = TC_createArrayObject(context, BYTE_ARRAY, 1))) - goto finish; - TC_setObjectLock(p->retO, UNLOCKED); - ARRAYOBJ_START(p->retO)[0] = table->primaryKeyCol; - } - else if (table->composedPK != NO_PRIMARY_KEY) // Composed primary key. - { - if (!(p->retO = TC_createArrayObject(context, BYTE_ARRAY, table->numberComposedPKCols))) - goto finish; - TC_setObjectLock(p->retO, UNLOCKED); - xmemmove(ARRAYOBJ_START(p->retO), table->composedPrimaryKeyCols, table->numberComposedPKCols); - } - else - p->retO = null; - } - else // The table name can't be null. - TC_throwNullArgumentException(context, "tableName"); - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/ResultSetMetaData public native String[] getPKColumnNames(String tableName) throws NullPointerException; -/** - * Returns the primary key column names of a table. - * - * @param p->obj[1] The table name. - * @param p->retO null if the given table does not have primary key or an array with the column names of the primary key. - * @throws NullPointerException if the table name is null. - */ -LB_API void lRSMD_getPKColumnNames_s(NMParams p) -{ - TRACE("lRSMD_getPKColumnNames_s") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - Context context = p->currentContext; - - MEMORY_TEST_START - - if (testRSClosed(context, resultSet)) // The driver and the result set can't be closed. - { - TCObject tableNameStr = p->obj[1]; - - if (tableNameStr) - { - ResultSet* rsBag = getResultSetBag(resultSet); - char tableNameCharP[DBNAME_SIZE]; - Table* table; - - // Gets the table given its name in the result set. - TC_JCharP2CharPBuf(String_charsStart(tableNameStr), String_charsLen(tableNameStr), tableNameCharP); - TC_CharPToLower(tableNameCharP); - if (!(table = getTableRS(context, rsBag, tableNameCharP))) - goto finish; - - if (table->primaryKeyCol != NO_PRIMARY_KEY) // Simple primary key. - { - TCObject* array; - - if (!(p->retO = TC_createArrayObject(context, "[java.lang.String", 1))) - goto finish; - TC_setObjectLock(p->retO, UNLOCKED); - - array = (TCObject*)ARRAYOBJ_START(p->retO); - if (!(array[0] = TC_createStringObjectFromCharP(context, table->columnNames[table->primaryKeyCol], -1))) - goto finish; - TC_setObjectLock(array[0], UNLOCKED); - } - else if (table->composedPK != NO_PRIMARY_KEY) // Composed primary key. - { - TCObject* array; - int32 i = table->numberComposedPKCols; - uint8* composedPKCols = table->composedPrimaryKeyCols; - CharP* columnNames = table->columnNames; - - if (!(p->retO = TC_createArrayObject(context, "[java.lang.String", i))) - goto finish; - TC_setObjectLock(p->retO, UNLOCKED); - array = (TCObject*)ARRAYOBJ_START(p->retO); - - while (--i >= 0) - { - if (!(array[i] = TC_createStringObjectFromCharP(context, columnNames[composedPKCols[i]], -1))) - goto finish; - TC_setObjectLock(array[i], UNLOCKED); - } - } - else - p->retO = null; - } - else // The table name can't be null. - TC_throwNullArgumentException(context, "tableName"); - } - -finish: ; - MEMORY_TEST_END -} - -// juliana@253_4: added methods to return the default value of a column. -////////////////////////////////////////////////////////////////////////// -// litebase/ResultSetMetaData public native String getDefaultValue(int columnIndex) throws DriverException; -/** - * Returns the default value of a column. - * - * @param p->i32[0] The column index. - * @return p->retO receives the default value of the column as a string or null if there is no default value. - * @throws DriverException If the column index does not have an underlining table. - */ -LB_API void lRSMD_getDefaultValue_i(NMParams p) -{ - TRACE("lRSMD_getDefaultValue_i") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]), - nameObj = null; - Context context = p->currentContext; - - MEMORY_TEST_START - - lRSMD_getColumnTableName_i(p); // It already tests if the result set is valid. - - if (!context->thrownException && (nameObj = p->retO)) - { - ResultSet* rsBag = getResultSetBag(resultSet); - char nameCharP[DBNAME_SIZE]; - SQLResultSetField* field = rsBag->selectClause->fieldList[p->i32[0] - 1]; - - // Gets the table column info. - TC_JCharP2CharPBuf(String_charsStart(nameObj), String_charsLen(nameObj), nameCharP); - - // Returns the default value of the column or the parameter of a function. - TC_setObjectLock(p->retO = getDefault(context, rsBag, nameCharP, field->parameter? field->parameter->tableColIndex : field->tableColIndex), UNLOCKED); - } - else if (!nameObj) // The column does not have an underlining table. - { - IntBuf buffer; - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), TC_int2str(p->i32[0], buffer)); - } -} - -////////////////////////////////////////////////////////////////////////// -// litebase/ResultSetMetaData public native String getDefaultValue(String columnName) throws DriverException, NullPointerException; -/** - * Returns the default value of a column. - * - * @param p->obj[1] The column name. - * @return p->retO receives the default value of the column as a string or null if there is no default value. - * @throws DriverException If the column name does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_getDefaultValue_s(NMParams p) -{ - TRACE("lRSMD_getDefaultValue_s") - TCObject resultSet = OBJ_ResultSetMetaData_ResultSet(p->obj[0]); - Context context = p->currentContext; - - MEMORY_TEST_START - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - ResultSet* rsBag = getResultSetBag(resultSet); - TCObject columnNameStr = p->obj[1]; - - if (columnNameStr) - { - SQLResultSetField** fields = rsBag->selectClause->fieldList; - SQLResultSetField* field; - JCharP columnNameJCharP = String_charsStart(columnNameStr); - int32 i = -1, - length = rsBag->selectClause->fieldsCount, - columnNameLength = String_charsLen(columnNameStr); - CharP tableColName; - - p->retO = null; - - while (++i < length) // Gets the name of the table or its alias given the column name. - { - if ((((tableColName = (field = fields[i])->tableColName) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - || ((tableColName = fields[i]->alias) - && JCharPEqualsCharP(columnNameJCharP, tableColName, columnNameLength, xstrlen(tableColName), true))) - { - if (field->tableName) // Returns the default value of the column or the parameter of a function. - TC_setObjectLock(p->retO = getDefault(context, rsBag, field->tableName, field->parameter? field->parameter->tableColIndex : field->tableColIndex), UNLOCKED); - else - i = length; - break; - } - } - if (i == length) // Column name or alias not found. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), - (tableColName = TC_JCharP2CharP(columnNameJCharP, columnNameLength))? tableColName : ""); - xfree(tableColName); - } - } - else // The column name can't be null. - TC_throwNullArgumentException(context, "columnName"); - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// guich@550_43: fixed problem when reusing the statement. -// litebase/PreparedStatement public native litebase.ResultSet executeQuery() throws DriverException, OutOfMemoryError; -/** - * This method executes a prepared SQL query and returns its ResultSet. - * - * @param p->obj[0] The prepared statement. - * @param p->retO receives the ResultSet of the SQL statement. - * @throws DriverException If the statement to be execute is not a select or there are undefined parameters. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_executeQuery(NMParams p) -{ - TRACE("lPS_executeQuery") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - Context context = p->currentContext; - - if (OBJ_PreparedStatementType(stmt) != CMD_SELECT) // The statement must be a select. - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_QUERY_DOESNOT_RETURN_RESULTSET)); - else - { - SQLSelectStatement* selectStmt = (SQLSelectStatement*)getPreparedStatementStatement(stmt); // The select statement. - TCObject driver = OBJ_PreparedStatementDriver(stmt); - - if (!allParamValuesDefinedSel(selectStmt)) // All the parameters of the select statement must be defined. - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_NOT_ALL_PARAMETERS_DEFINED)); - else - { - ResultSet* resultSetBag; - SQLSelectClause* selectClause = selectStmt->selectClause; - Heap heap = selectClause->heap; - bool locked = false; - PlainDB* plainDB; - TCObject logger = litebaseConnectionClass->objStaticValues[1]; - - // juliana@253_18: now it is possible to log only changes during Litebase operation. - if (logger && !litebaseConnectionClass->i32StaticValues[6]) // If log is on, adds information to it. - { - LOCKVAR(log); - if (OBJ_PreparedStatementStoredParams(stmt)) - { - TCObject string = toStringBuffer(context, stmt); - if (string) - TC_executeMethod(context, loggerLogInfo, logger, string); // juliana@230_30 - } - else - TC_executeMethod(context, loggerLog, logger, 16, OBJ_PreparedStatementSqlExpression(stmt), false); - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - resetWhereClause(selectStmt->whereClause, heap); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto finish; - } - - // guich@554_37: tableColIndex may change between runs of a prepared statement with a sort field so we have to cache the tableColIndex of - // the order by fields. - resetColumnListClause(selectStmt->orderByClause); - - // juliana@226_14: corrected a bug that would make a prepared statement with group by not work correctly after the first execution. - resetColumnListClause(selectStmt->groupByClause); - - selectClause->isPrepared = true; - TC_setObjectLock(p->retO = litebaseDoSelect(context, driver, selectStmt), UNLOCKED); - - if (p->retO) - { - // Gets the query result table size and stores it. - locked = true; - LOCKVAR(parser); - resultSetBag = (ResultSet*)getResultSetBag(p->retO); - plainDB = &resultSetBag->table->db; - if (!muPut(&memoryUsage, selectStmt->selectClause->sqlHashCode, plainDB->db.size, plainDB->dbo.size)) - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - UNLOCKVAR(parser); - locked = false; - } - } - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * This method executes a SQL INSERT, UPDATE, or DELETE statement. SQL statements that return nothing such as - * SQL DDL statements can also be executed. - * - * @param p->obj[0] The prepared statement. - * @param p->retI receives the result is either the row count for INSERT, UPDATE, or DELETE statements; or 0 - * for SQL statements that return nothing. - * @throws DriverException If the query does not update the table or there are undefined parameters. - */ -LB_API void lPS_executeUpdate(NMParams p) // litebase/PreparedStatement public native int executeUpdate() throws DriverException; -{ - TRACE("lPS_executeUpdate") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - Context context = p->currentContext; - - if (OBJ_PreparedStatementType(stmt) == CMD_SELECT) // The statement must be a select. - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_QUERY_DOESNOT_PERFORM_UPDATE)); - else - { - TCObject logger = litebaseConnectionClass->objStaticValues[1], - driver = OBJ_PreparedStatementDriver(stmt); - - if (logger) // If log is on, adds information to it. - { - LOCKVAR(log); - if (OBJ_PreparedStatementStoredParams(stmt)) - { - TCObject string = toStringBuffer(context, stmt); - if (string) - TC_executeMethod(context, loggerLogInfo, logger, string); // juliana@230_30 - } - else - TC_executeMethod(context, loggerLog, logger, 16, OBJ_PreparedStatementSqlExpression(stmt), false); - - UNLOCKVAR(log); - if (context->thrownException) - goto finish; - } - - // juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first - // execution. - switch (OBJ_PreparedStatementType(stmt)) // Returns the number of rows affected or if the command was successfully executed. - { - case CMD_INSERT: - { - SQLInsertStatement* insertStmt = (SQLInsertStatement*)getPreparedStatementStatement(stmt); - - rearrangeNullsInTable(insertStmt->table, insertStmt->record, insertStmt->storeNulls, insertStmt->paramDefined, - insertStmt->paramIndexes, insertStmt->nFields, insertStmt->paramCount); - if (convertStringsToValues(context, insertStmt->table, insertStmt->record, insertStmt->nFields)) - p->retI = litebaseDoInsert(context, insertStmt); - break; - } - case CMD_UPDATE: - { - SQLUpdateStatement* updateStmt = (SQLUpdateStatement*)getPreparedStatementStatement(stmt); - - resetWhereClause(updateStmt->whereClause, updateStmt->heap); // guich@554_13 - rearrangeNullsInTable(updateStmt->rsTable->table, updateStmt->record, updateStmt->storeNulls, updateStmt->paramDefined, - updateStmt->paramIndexes, updateStmt->nValues, updateStmt->paramCount); - if (allParamValuesDefinedUpd(updateStmt) - && convertStringsToValues(context, updateStmt->rsTable->table, updateStmt->record, updateStmt->nValues)) - p->retI = litebaseDoUpdate(context, updateStmt); - break; - } - case CMD_DELETE: - { - SQLDeleteStatement* deleteStmt = (SQLDeleteStatement*)getPreparedStatementStatement(stmt); - - resetWhereClause(deleteStmt->whereClause, deleteStmt->heap); // guich@554_13 - if (allParamValuesDefinedDel(deleteStmt)) - p->retI = litebaseDoDelete(context, deleteStmt); - break; - } - case CMD_CREATE_TABLE: - { - TCObject sqlExpression = OBJ_PreparedStatementSqlExpression(stmt); - - litebaseExecute(context, driver, String_charsStart(sqlExpression), String_charsLen(sqlExpression)); - p->retI = 0; - break; - } - default: // alter table or drop - { - TCObject sqlExpression = OBJ_PreparedStatementSqlExpression(stmt); - p->retI = litebaseExecuteUpdate(context, driver, String_charsStart(sqlExpression), String_charsLen(sqlExpression)); - } - } - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/PreparedStatement public native void setShort(int index, short value); -/** - * This method sets the specified parameter from the given Java short value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i32[1] The value of the parameter. - */ -LB_API void lPS_setShort_is(NMParams p) -{ - TRACE("lPS_setShort_is") - MEMORY_TEST_START - psSetNumericParamValue(p, SHORT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/PreparedStatement public native void setInt(int index, int value); -/** - * This method sets the specified parameter from the given Java int value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i32[1] The value of the parameter. - */ -LB_API void lPS_setInt_ii(NMParams p) -{ - TRACE("lPS_setInt_ii") - MEMORY_TEST_START - psSetNumericParamValue(p, INT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/PreparedStatement public native void setLong(int index, long value); -/** - * This method sets the specified parameter from the given Java long value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i64[0] The value of the parameter. - */ -LB_API void lPS_setLong_il(NMParams p) -{ - TRACE("lPS_setLong_il") - MEMORY_TEST_START - psSetNumericParamValue(p, LONG_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/PreparedStatement public native void setFloat(int index, float value); -/** - * This method sets the specified parameter from the given Java float value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->dbl[0] The value of the parameter. - */ -LB_API void lPS_setFloat_id(NMParams p) -{ - TRACE("lPS_setFloat_id") - MEMORY_TEST_START - psSetNumericParamValue(p, FLOAT_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// litebase/PreparedStatement public native void setDouble(int index, double value); -/** - * This method sets the specified parameter from the given Java double value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->dbl[0] The value of the parameter. - */ -LB_API void lPS_setDouble_id(NMParams p) -{ - TRACE("lPS_setDouble_id") - MEMORY_TEST_START - psSetNumericParamValue(p, DOUBLE_TYPE); - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * This method sets the specified parameter from the given Java String value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. DO NOT SURROUND IT WITH '!. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_setString_is(NMParams p) // litebase/PreparedStatement public native void setString(int index, String value) OutOfMemoryError; -{ - TRACE("lPS_setString_is") - - MEMORY_TEST_START - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (statement) // Only sets the parameter if the statement is not null. - { - TCObject string = p->obj[1]; - int32 index = p->i32[0]; - - // juliana@238_1: corrected the end quote not appearing in the log files after dates. - // juliana@222_8: stores the object so that it won't be collected. - if (psSetStringParamValue(p->currentContext, stmt, string, index, string? String_charsLen(string) : 0)) // Sets the string parameter. - ((TCObject*)ARRAYOBJ_START(OBJ_PreparedStatementObjParams(stmt)))[index] = string; - } - } - - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * This method sets the specified parameter from the given array of bytes as a blob. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - * @throws SQLParseException If the parameter to be set is in the where clause. - */ -LB_API void lPS_setBlob_iB(NMParams p) // litebase/PreparedStatement public native void setBlob(int index, uint8 []value) throws SQLParseException; -{ - TRACE("lPS_setBlob_iB") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (statement) // Only sets the parameter if the statement is not null. - { - TCObject blob = p->obj[1]; - TCObject* objParams = (TCObject*)ARRAYOBJ_START(OBJ_PreparedStatementObjParams(stmt)); - uint8* blobArray = null; - int32 index = p->i32[0], - blobLength = 0; - - if (blob) - { - blobLength = ARRAYOBJ_LEN(p->obj[1]); - blobArray = (uint8*)ARRAYOBJ_START(p->obj[1]); - } - - switch (statement->type) // Sets the parameter. - { - case CMD_INSERT: - if (!setStrBlobParamValueIns(p->currentContext, (SQLInsertStatement*)statement, index, blobArray, blobLength, false)) - goto finish; - break; - case CMD_UPDATE: - if (!setStrBlobParamValueUpd(p->currentContext, (SQLUpdateStatement*)statement, index, blobArray, blobLength, false)) - goto finish; - break; - - // A blob can't be used in a where clause. - case CMD_SELECT: - case CMD_DELETE: - TC_throwExceptionNamed(p->currentContext, "litebase.SQLParseException", getMessage(ERR_BLOB_WHERE)); - goto finish; - } - - objParams[index] = p->obj[1]; // juliana@222_8: stores the object so that it won't be collected. - - if (OBJ_PreparedStatementStoredParams(stmt)) // Only stores the parameter if there are parameters to be stored. - { - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(stmt); - - if (blob) // The parameter is not null. - TC_CharP2JCharPBuf("[BLOB]", 6, paramsAsStrs[index], true); - else // The parameter is null; - TC_CharP2JCharPBuf("null", 4, paramsAsStrs[index], true); - } - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/PreparedStatement public native void setDate(int index, totalcross.Util.Date) throws OutOfMemoryError; -/** - * This method sets the specified parameter from the given Java Date value formated as "YYYY/MM/DD"
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, according - * to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, can generate - * a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date format - * parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_setDate_id(NMParams p) -{ - TRACE("lPS_setDate_id") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (statement) // Only sets the parameter if the statement is not null. - { - Context context = p->currentContext; - TCObject date = p->obj[1]; - TCObject* objParams = (TCObject*)ARRAYOBJ_START(OBJ_PreparedStatementObjParams(stmt)); - JCharP stringChars = null; - int32 index = p->i32[0]; - TCObject dateBufObj = objParams[index]; - - // juliana@238_1: corrected the end quote not appearing in the log files after dates. - if (date) - { - if (!dateBufObj || String_charsLen(dateBufObj) < 10) - { - if (!(dateBufObj = TC_createStringObjectWithLen(context, 10))) - goto finish; - TC_setObjectLock(dateBufObj, UNLOCKED); - objParams[index] = dateBufObj; // juliana@222_8: stores the object so that it won't be collected. - } - else - xmemzero(String_charsStart(dateBufObj), String_charsLen(dateBufObj) << 1); - date2JCharP(FIELD_I32(date, 2), FIELD_I32(date, 1), FIELD_I32(date, 0), stringChars = String_charsStart(dateBufObj)); - } - - psSetStringParamValue(p->currentContext, stmt, dateBufObj, index, 10); // Sets the string parameter. - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * This method sets the specified parameter from the given Java DateTime value formated as "YYYY/MM/DD HH:MM:SS:ZZZ".
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, according - * to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, can generate - * a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date format - * parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - */ -LB_API void lPS_setDateTime_id(NMParams p) // litebase/PreparedStatement public native void setDate(int index, totalcross.Util.Date); -{ - TRACE("lPS_setDateTime_id") - lPS_setDate_id(p); -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// litebase/PreparedStatement public native void setDateTime(int index, totalcross.sys.Time) throws OutOfMemoryError; -/** - * Formats the Time t into a string "YYYY/MM/DD HH:MM:SS:ZZZ" - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_setDateTime_it(NMParams p) -{ - TRACE("lPS_setDateTime_it") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (statement) // Only sets the parameter if the statement is not null. - { - Context context = p->currentContext; - TCObject time = p->obj[1]; - TCObject* objParams = (TCObject*)ARRAYOBJ_START(OBJ_PreparedStatementObjParams(stmt)); - JCharP stringChars = null; - int32 index = p->i32[0]; - TCObject dateTimeBufObj = objParams[index]; - - // juliana@238_1: corrected the end quote not appearing in the log files after dates. - if (time) - { - if (!dateTimeBufObj || String_charsLen(dateTimeBufObj) < 23) - { - if (!(dateTimeBufObj = TC_createStringObjectWithLen(context, 23))) - goto finish; - TC_setObjectLock(dateTimeBufObj, UNLOCKED); - objParams[index] = dateTimeBufObj; // juliana@222_8: stores the object so that it won't be collected. - } - else - xmemzero(String_charsStart(dateTimeBufObj), String_charsLen(dateTimeBufObj) << 1); - dateTime2JCharP(Time_year(time), Time_month(time), Time_day(time), Time_hour(time), Time_minute(time), Time_second(time), Time_millis(time), stringChars = String_charsStart(dateTimeBufObj)); - } - - psSetStringParamValue(p->currentContext, stmt, dateTimeBufObj, index, 23); // Sets the string parameter. - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@223_3: PreparedStatement.setNull() now works for blobs. -/** - * Sets null in a given field. This can be used to set any column type as null. It must be just remembered that a parameter in a where clause can't - * be set to null. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set as null, starting from 0. - * @throws SQLParseException If the parameter to be set as null is in the where clause. - */ -LB_API void lPS_setNull_i(NMParams p) // litebase/PreparedStatement public native void setNull(int index) throws SQLParseException; -{ - TRACE("lPS_setNull_i") - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (statement) // Only sets the parameter if the statement is not null. - { - int32 index = p->i32[0]; - - switch (statement->type) - { - case CMD_INSERT: - if (!setNullIns(p->currentContext, (SQLInsertStatement*)statement, index)) - goto finish; - break; - case CMD_DELETE: - case CMD_SELECT: - TC_throwExceptionNamed(p->currentContext, "litebase.SQLParseException", getMessage(ERR_PARAM_NULL)); - goto finish; - break; - case CMD_UPDATE: - if (!setNullUpd(p->currentContext, (SQLUpdateStatement*)statement, index)) - goto finish; - } - if (OBJ_PreparedStatementStoredParams(stmt)) - TC_CharP2JCharPBuf("null", 4, getPreparedStatementParamsAsStrs(stmt)[index], true); - } - } - -finish: ; - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * This method clears all of the input parameters that have been set on this statement. - * - * @param p->obj[0] The prepared statement. - */ -LB_API void lPS_clearParameters(NMParams p) // litebase/PreparedStatement public native void clearParamValues(); -{ - TRACE("lPS_clearParameters") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (statement) // Only clears the parameter if the statement is not null. - { - int32 length = OBJ_PreparedStatementStoredParams(stmt); - - if (length) - { - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(stmt); - - while (--length >= 0) - TC_CharP2JCharPBuf("unfilled", 8, paramsAsStrs[length], true); - } - - switch (statement->type) - { - case CMD_DELETE: - clearParamValuesDel((SQLDeleteStatement*)statement); - break; - case CMD_INSERT: - clearParamValuesIns((SQLInsertStatement*)statement); - break; - case CMD_SELECT: - clearParamValuesSel(statement); - break; - case CMD_UPDATE: - clearParamValuesUpd((SQLUpdateStatement*)statement); - } - } - } - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -/** - * Returns the sql used in this statement. If logging is disabled, returns the sql without the arguments. If logging is enabled, returns the real - * sql, filled with the arguments. - * - * @param p->obj[0] The prepared statement. - * @param p->obj[0] receives the sql used in this statement. - */ -LB_API void lPS_toString(NMParams p) // litebase/PreparedStatement public native String toString(); -{ - TRACE("lPS_toString") - - MEMORY_TEST_START - - if (testPSClosed(p)) - { - TCObject statement = p->obj[0]; - - if (OBJ_PreparedStatementStoredParams(statement)) // There are no parameters o the logger is not being used. - { - TCObject string; - int16* paramsPos = getPreparedStatementParamsPos(statement); - JCharP sql = String_charsStart(OBJ_PreparedStatementSqlExpression(statement)), - charsStart; - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(statement); - - // juliana@202_16: Now prepared statement logging is equal in all platfotms. - int32 debugLen = 6 + paramsPos[0], - storedParams = OBJ_PreparedStatementStoredParams(statement), - i = -1, - length; - - // juliana@202_15: Corrected a bug that would cause a gpf or a reset when logging a prepared statement with a null value. - while (++i < storedParams) - debugLen += TC_JCharPLen(paramsAsStrs[i]) + paramsPos[i + 1] - paramsPos[i] - 1; - - // juliana@230_30: reduced log files size. - if (!(p->retO = string = TC_createStringObjectWithLen(p->currentContext, debugLen))) - goto finish; - TC_setObjectLock(p->retO, UNLOCKED); - - // PREP: + string before the first '?'. - TC_CharP2JCharPBuf("PREP: ", 6, (charsStart = String_charsStart(string)), false); - xmemmove(&charsStart[6], sql, paramsPos[0] << 1); - debugLen = 6 + paramsPos[0]; - i = -1; - - while (++i < storedParams) // Concatenates each string part with the next parameter. - { - xmemmove(&charsStart[debugLen], paramsAsStrs[i], (length = TC_JCharPLen(paramsAsStrs[i])) << 1); - debugLen += length; - xmemmove(&charsStart[debugLen], &sql[paramsPos[i] + 1], (length = (paramsPos[i + 1] - paramsPos[i] - 1)) << 1); - debugLen += length; - } - } - else - p->retO = OBJ_PreparedStatementSqlExpression(statement); - } - -finish: ; - MEMORY_TEST_END -} - -// juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - -////////////////////////////////////////////////////////////////////////// -// juliana@253_20: added PreparedStatement.close(). -/** - * Closes a prepared statement. - * - * @param p->obj[0] The prepared statement. - */ -LB_API void lPS_close(NMParams p) // litebase/PreparedStatement public native void close(); -{ - TRACE("lPS_close") - MEMORY_TEST_START - if (testPSClosed(p)) - { - TCObject statement = p->obj[0]; - Hashtable* htPS = getLitebaseHtPS(OBJ_PreparedStatementDriver(statement)); - TCObject sqlExpression = OBJ_PreparedStatementSqlExpression(statement); - int32 hashCode = TC_JCharPHashCode(String_charsStart(sqlExpression), String_charsLen(sqlExpression)); - TC_htRemove(htPS, hashCode); - freePreparedStatement(0, statement); - } - MEMORY_TEST_END -} - -////////////////////////////////////////////////////////////////////////// -// juliana@253_21: added PreparedStatement.isValid(). -/** - * Indicates if a prepared statement is valid or not: the driver is open and its SQL is in the hash table. - * - * @param p->obj[0] The prepared statement. - * @param p->retI receives true if the prepared statement is valid; false, otherwise. - */ -LB_API void lPS_isValid(NMParams p) // litebase/PreparedStatement public native boolean isValid(); -{ - TRACE("lPS_isValid") - TCObject statement = p->obj[0]; - - MEMORY_TEST_START - - // Tests if the prepared statement isclosed or The connection with Litebase is closed. - if (OBJ_PreparedStatementDontFinalize(statement) || OBJ_LitebaseDontFinalize(OBJ_PreparedStatementDriver(statement))) // The connection with Litebase can't be closed. - p->retI = false; - else - p->retI = true; - - MEMORY_TEST_END -} diff --git a/LitebaseSDK/src/native/NativeMethods.h b/LitebaseSDK/src/native/NativeMethods.h deleted file mode 100644 index 730f3b819b..0000000000 --- a/LitebaseSDK/src/native/NativeMethods.h +++ /dev/null @@ -1,1199 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares Litebase native methods. - */ - -#ifndef LITEBASE_NATIVEMETHODS_H -#define LITEBASE_NATIVEMETHODS_H - -#include "Litebase.h" - -/** - * Moves to the next record and fills the data members. - * - * @param p->obj[0] The row iterator. - * @param p->retI Receives true if it is possible to iterate to the next record. Otherwise, it will return false. - */ -LB_API void lRI_next(NMParams p); - -/** - * Moves to the next record with an attribute different of SYNCED. - * - * @param p->obj[0] The row iterator. - * @param p->retI Receives true if it is possible to iterate to a next record not synced. Otherwise, it will return false. - */ -LB_API void lRI_nextNotSynced(NMParams p); - -/** - * If the attribute is currently NEW or UPDATED, this method sets them to SYNCED. Note that if the row is DELETED, the change will be ignored. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_setSynced(NMParams p); - -/** - * Forces the attribute to be NEW. This method will be useful if a row was marked as synchronized but was not sent to server for some problem. - * If the row is marked as DELETED, its attribute won't be changed. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_setNotSynced(NMParams p); // juliana@270_29: added RowIterator.setNotSynced(). - -/** - * Closes this iterator. - * - */ -LB_API void lRI_close(NMParams p); - -/** - * Resets the counter to zero so it is possible to restart to fetch records. - * - * @param p->obj[0] The row iterator. - */ -LB_API void lRI_reset(NMParams p); - -/** - * Returns a short contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The short column index, starting from 1. - * @param p->retI Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getShort_i(NMParams p); - -/** - * Returns an integer contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The integer column index, starting from 1. - * @param p->retI Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getInt_i(NMParams p); - -/** - * Returns a long integer contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The long integer column index, starting from 1. - * @param p->retL Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getLong_i(NMParams p); - -/** - * Returns a floating point number contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The floating point number column index, starting from 1. - * @param p->retD Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getFloat_i(NMParams p); - -/** - * Returns a double precision floating point number contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The double precision floating point number column index, starting from 1. - * @param p->retD Receives the value of the column or 0 if the column is null. - */ -LB_API void lRI_getDouble_i(NMParams p); - -/** - * Returns a string contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The string column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getString_i(NMParams p); - -/** - * Returns a blob contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The blob column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getBlob_i(NMParams p); - -/** - * Returns a date contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The date column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getDate_i(NMParams p); - -/** - * Returns a datetime contained in the current row. - * - * @param p->obj[0] The row iterator. - * @param p->i32[0] The datetime column index, starting from 1. - * @param p->retO Receives the value of the column or null if the column is null. - */ -LB_API void lRI_getDateTime_i(NMParams p); - -/** - * Indicates if this column has a NULL. - * - * @param p->i32[0] The column index, starting from 1. - * @param p->retI Receives true if the value is SQL NULL; false, otherwise. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRI_isNull_i(NMParams p); - -/** - * Creates a Litebase connection for the default creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param p->retO Receives a Litebase instance. - */ -LB_API void lLC_privateGetInstance(NMParams p); - -/** - * Creates a Litebase connection for the given creator id, storing the database as a flat file. This method avoids the creation of more than one - * instance with the same creator id, which would lead to performance and memory problems. Using this method, the strings are stored in the - * unicode format. - * - * @param p->obj[0] The creator id, which may (or not) be the same one of the current application and MUST be 4 characters long. - * @param p->retO Receives a Litebase instance. - * @throws DriverException If an application id with more or less than four characters is specified. - * @throws NullPointerException If appCrid == null. - */ -LB_API void lLC_privateGetInstance_s(NMParams p); - -/** - * Creates a connection with Litebase. - * - * @param p->obj[0] The creator id, which may be the same one of the current application. - * @param p->obj[1] Only the folder where it is desired to store the tables, null, if it is desired to use the current data - * path, or chars_type = chars_format; path = source_path[;crypto] , where chars_format can be ascii or - * unicode, source_path is the folder where the tables will be stored, and crypto must be used if the tables of the - * connection use cryptography. The params can be entered in any order. If only the path is passed as a parameter, unicode is used and there is no - * cryptography. Notice that path must be absolute, not relative. - *

    Note that databases belonging to multiple applications can be stored in the same path, since all tables are prefixed by the application's - * creator id. - *

    Also notice that to store Litebase files on card on Pocket PC, just set the second parameter to the correct directory path. - *

    It is not recommended to create the databases directly on the PDA. Memory cards are FIVE TIMES SLOWER than the main memory, so it will take - * a long time to create the tables. Even if the NVFS volume is used, it can be very slow. It is better to create the tables on the desktop, and copy - * everything to the memory card or to the NVFS volume. - *

    Due to the slowness of a memory card and the NVFS volume, all queries will be stored in the main memory; only tables and indexes will be stored - * on the card or on the NVFS volume. - *

    An exception will be raised if tables created with an ascii kind of connection are oppened with an unicode connection and vice-versa. - * @param p->retO Receives a Litebase instance. - * @throws DriverException If an application id with more or less than four characters is specified. - * @throws NullPointerException If appCrid == null. - */ -LB_API void lLC_privateGetInstance_ss(NMParams p); - -/** - * Returns the path where the tables created/opened by this connection are stored. - * - * @param p->obj[0] The connection with Litebase. - * @param p->retO Receives a string representing the path. - * @throws IllegalStateException If the driver is closed. - */ -LB_API void lLC_getSourcePath(NMParams p); - -/** - * Used to execute a create table or create index SQL commands. - * - *

    Examples: - *

      - *
    • driver.execute("create table PERSON (NAME CHAR(30), SALARY DOUBLE, AGE INT, EMAIL CHAR(50))"); - *
    • driver.execute("CREATE INDEX IDX_NAME ON PERSON(NAME)"); - *
    - * - *

    When creating an index, its name is ignored but must be given. The index can be created after data was added to the table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL creation command. - */ -LB_API void lLC_execute_s(NMParams p); - -/** - * Used to execute updates in a table (insert, delete, update, alter table, drop). E.g.: - * - *

    driver.executeUpdate("drop table person"); will drop also the indices. - *

    driver.executeUpdate("drop index * on person"); will drop all indices but not the primary key index. - *

    driver.executeUpdate("drop index name on person"); will drop the index for the "name" column. - *

    driver.executeUpdate("ALTER TABLE person DROP primary key"); will drop the primary key. - *

    driver.executeUpdate("update person set age=44, salary=3200.5 where name = 'guilherme campos hazan'"); will update the - * table. - *

    driver.executeUpdate("delete person where name like 'g%'"); will delete records of the table. - *

    driver.executeUpdate("insert into person (age, salary, name, email) - * values (32, 2000, 'guilherme campos hazan', 'guich@superwaba.com.br')"); will insert a record in the table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL update command. - * @param p->retI Receives the number of rows affected or 0 if a drop or alter operation was successful. - */ -LB_API void lLC_executeUpdate_s(NMParams p); - -/** - * Used to execute queries in a table. Example: - * - *

    - * ResultSet rs = driver.executeQuery("select rowid, name, salary, age from person where age != 44");
    - * rs.afterLast();
    - * while (rs.prev())
    - *    Vm.debug(rs.getString(1) + ". " + rs.getString(2) + " - " + rs.getInt("age") + " years");
    - * 
    - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL query command. - * @param p->retO Receives a result set with the values returned from the query. - */ -LB_API void lLC_executeQuery_s(NMParams p); - -/** - * Creates a pre-compiled statement with the given sql. Prepared statements are faster for repeated queries. Instead of parsing the same query - * where only a few arguments change, it is better to create a prepared statement and the query is pre-parsed. Then, it is just needed to set the - * arguments (defined as ? in the sql) and run the sql. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The SQL query command. - * @param p->retO Receives a pre-compiled SQL statement. - * @throws OutOfMemoryError If there is not enough memory to create the preparedStatement. - */ -LB_API void lLC_prepareStatement_s(NMParams p); - -/** - * Returns the current rowid for a given table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives the current rowid for the table. - */ -LB_API void lLC_getCurrentRowId_s(NMParams p); - -/** - * Returns the number of valid rows in a table. This may be different from the number of records if a row has been deleted. - * - * @see #getRowCountDeleted(String) - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives the number of valid rows in a table. - */ -LB_API void lLC_getRowCount_s(NMParams p); - -/** - * Sets the row increment used when creating or updating big amounts of data. Using this method greatly increases the speed of bulk insertions - * (about 3x faster). To use it, it is necessary to call it (preferable) with the amount of lines that will be inserted. After the insertion is - * finished, it is NECESSARY to call it again, passing -1 as the increment argument. Without doing this last step, data may be - * lost because some writes will be delayed until the method is called with -1. Another good optimization on bulk insertions is to drop the indexes - * and then create them afterwards. So, to correctly use setRowInc(), it is necessary to: - * - *
    - * driver.setRowInc("table", totalNumberOfRows);
    - * // Fetch the data and insert them.
    - * driver.setRowInc("table", -1);
    - * 
    - * - * Using prepared statements on insertion makes it another a couple of times faster. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->i32[0] The increment value. - * @throws IllegalArgumentException If the increment is equal to 0 or less than -1. - */ -LB_API void lLC_setRowInc_si(NMParams p); - -/** - * Indicates if the given table already exists. This method can be used before a drop table. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives true if a table exists; false othewise. - * @throws DriverException If tableName or path is too big. - */ -LB_API void lLC_exists_s(NMParams p); - -/** - * Releases the file handles (on the device) of a Litebase instance. Note that, after this is called, all Resultsets and - * PreparedStatements created with this Litebase instance will be in an inconsistent state, and using them will probably reset the - * device. This method also deletes the active instance for this creator id from Litebase's internal table. - * - * @param p->obj[0] The connection with Litebase. - * @throws IllegalStateException If the driver is closed. - */ -LB_API void lLC_closeAll(NMParams p); - -/** - * Used to delete physically the records of the given table. Records are always deleted logically, to avoid the need of recreating the indexes. When - * a new record is added, it doesn't uses the position of the previously deleted one. This can make the table big, if a table is created, filled and - * has a couple of records deleted. This method will remove all deleted records and recreate the indexes accordingly. Note that it can take some time - * to run. - *

    - * Important: the rowid of the records is NOT changed with this operation. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The table name to purge. - * @param p->retI Receives the number of purged records. - * @throws DriverException If a row can't be read or written. - * @throws OutOfMemoryError If there is not enough memory to purge the table. - */ -LB_API void lLC_purge_s(NMParams p); - -/** - * Returns the number of deleted rows. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retI Receives the total number of deleted records of the given table. - */ -LB_API void lLC_getRowCountDeleted_s(NMParams p); - -/** - * Gets an iterator for a table. With it, it is possible iterate through all the rows of a table in sequence and get its attributes. This is good for - * synchronizing a table. While the iterator is active, it is not possible to do any queries or updates because this can cause dada corruption. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of a table. - * @param p->retO receives a iterator for the given table. - */ -LB_API void lLC_getRowIterator_s(NMParams p); - -/** - * Gets the Litebase logger. The fields should be used unless using the logger within threads. - * - * @param p->retO receives the logger. - */ -LB_API void lLC_privateGetLogger(NMParams p); - -/** - * Sets the litebase logger. This enables log messages for all queries and statements of Litebase and can be very useful to help finding bugs in - * the system. Logs take up memory space, so turn them on only when necessary. The fields should be used unless using the logger within threads. - * - * @param p->obj[0] The logger. - */ -LB_API void lLC_privateSetLogger_l(NMParams p); - -/** - * Gets the default Litebase logger. When this method is called for the first time, a new text file is created. In the subsequent calls, the same - * file is used. - * - * @param p->retO receives the default logger. - * @throws DriverException if an IOException occurs. - */ -LB_API void lLC_privateGetDefaultLogger(NMParams p); - -/** - * Deletes all the log files with the default format found in the default device folder. If log is enabled, the current log file is not affected by - * this command. - * - * @param p->retI receives the number of files deleted. - */ -LB_API void lLC_privateDeleteLogFiles(NMParams p); - -/** - * This is a handy method that can be used to reproduce all commands of a log file. This is intended to be used by the development team only. - * Here's a sample on how to use it: - * - *

    - * String []sql =
    - * {
    - *    "new LitebaseConnection(MBSL,null)",
    - *    "create table PRODUTO (IDPRODUTO int, IDPRODUTOERP char(10), IDGRUPOPRODUTO int, IDSUBGRUPOPRODUTO int, IDEMPRESA char(20), 
    - *                                DESCRICAO char(100), UNDCAIXA char(10), PESO float, UNIDADEMEDIDA char(3),
    - *                                EMBALAGEM char(10), PORCTROCA float, PERMITETROCA int)",
    - *    "create index IDX_PRODUTO_1 on PRODUTO(IDPRODUTO)",
    - *    "create index IDX_PRODUTO_2 on PRODUTO(IDGRUPOPRODUTO)",
    - *    "create index IDX_PRODUTO_3 on PRODUTO(IDEMPRESA)",
    - *    "create index IDX_PRODUTO_4 on PRODUTO(DESCRICAO)",
    - *    "closeAll",
    - *    "new LitebaseConnection(MBSL,null)",
    - *    "insert into PRODUTO values(1,'19132', 2, 1, '1', 2, '3', 'ABSORVENTE SILHO ABAS', '5', 13, 'PCT', '20X30', 10, 0)",
    - *  };
    - *  LitebaseConnection.processLogs(sql, true);
    - * 
    - * - * @param p->obj[0] The string array of SQL commands to be executed. - * @param p->obj[1] The parameters to open a connection. - * @param p->i32[0] Indicates if debug information is to displayed on the debug console. - * @param p->retO Receives the LitebaseConnection instance created, or null if closeAll was the last command executed (or - * no commands were executed at all). - * @throws DriverException If an exception occurs. - * @throws NullPointerException If p->obj[0] is null. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_privateProcessLogs_Ssb(NMParams p); - -/** - * Tries to recover a table not closed properly by marking and erasing logically the records whose crc are not valid. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of the table to be converted. - * @param p->retI Receives the number of purged records. - * @throws DriverException If the table name or path is too big. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_recoverTable_s(NMParams p); - -/** - * Converts a table from the previous Litebase table version to the current one. If the table format is older than the previous table version, this - * method can't be used. It is possible to know if the table version is not compativel with the current version used in Litebase because an exception - * will be thrown if one tries to open a table with the old format. The table will be closed after using this method. Notice that the table .db file - * will be overwritten. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The name of the table to be converted. - * @throws DriverException If the table version is not the previous one (too old or the actual used by Litebase) or the table name or path is too big. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_convert_s(NMParams p); - -/** - * Used to returned the slot where the tables were stored on Palm OS. Not used anymore. - * - * @param p->retI receives -1. - */ -LB_API void lLC_getSlot(NMParams p); // juliana@223_1: added a method to get the current slot being used. - -// juliana@226_6: added LitebaseConnection.isOpen(), which indicates if a table is open in the current connection. -/** - * Indicates if a table is open or not. - * - * @param p->obj[0] The connection with Litebase. - * @param p->obj[1] The table name to be checked. - * @param p->retI receives true if the table is open in the current connection; false, otherwise. - * @throws DriverException If the table name is too big. - */ -LB_API void lLC_isOpen_s(NMParams p); - -/** - * Drops all the tables from a database represented by its application id and path. - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - * @throws DriverException If the database is not found or a file error occurs. - * @throws NullPointerException If one of the string parameters is null. - */ -LB_API void lLC_dropDatabase_ssi(NMParams p); - -/** - * Indicates if a table is closed properly or not. - * - * @param p->obj[1] The table to be verified. - * @param p->retI receives true if the table is closed properly or is open (a not properly closed table can't be opened); - * false, otherwise. - * @throws DriverException If the table is corrupted. - * @throws NullPointerException If tableName is null. - */ -LB_API void lLC_isTableProperlyClosed_s(NMParams p); - -/** - * Lists all table names of the current connection. - * - * @param p->retO receives an array of all the table names of the current connection. If the current connection has no tables, an empty list is - * returned. - * @throws DriverException If a file error occurs. - * @throws IllegalStateException If the driver is closed. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lLC_listAllTables(NMParams p); - -/** - * Encrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - */ -LB_API void lLC_encryptTables_ssi(NMParams p); - -/** - * Decrypts all the tables of a connection given from the application id. All the files of the tables must be closed! - * - * @param p->obj[0] The application id of the database. - * @param p->obj[1] The path where the files are stored. - */ -LB_API void lLC_decryptTables_ssi(NMParams p); - -/** - * Returns the metadata for this result set. - * - * @param p->obj[0] The result set. - * @param p->retO receives the metadata for this result set. - */ -LB_API void lRS_getResultSetMetaData(NMParams p); - -/** - * Releases all memory allocated for this object. Its a good idea to call this when you no longer needs it, but it is also called by the GC when the - * object is no longer in use. - * - * @param p->obj[0] The result set. - * @throws IllegalStateException If the result set is closed. - */ -LB_API void lRS_close(NMParams p); - -/** - * Places the cursor before the first record. - * - * @param p->obj[0] The result set. - */ -LB_API void lRS_beforeFirst(NMParams p); - -/** - * Places the cursor after the last record. - * - * @param p->obj[0] The result set. - */ -LB_API void lRS_afterLast(NMParams p); - -/** - * Places the cursor in the first record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if it was possible to place the cursor in the first record; false, otherwise. - */ -LB_API void lRS_first(NMParams p); - -/** - * Places the cursor in the last record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if it was possible to place the cursor in the last record; false, otherwise. - */ -LB_API void lRS_last(NMParams p); - -/** - * Gets the next record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if there is a next record to go to in the result set; false, otherwise. - */ -LB_API void lRS_next(NMParams p); - -/** - * Returns the previous record of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI Receives true if there is a previous record to go to in the result set; false, otherwise. - */ -LB_API void lRS_prev(NMParams p); - -/** - * Given the column index (starting from 1), returns a short value that is represented by this column. Note that it is only possible to request this - * column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getShort_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a short value that is represented by this column. Note that it is only possible to request this - * column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. This method - * is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getShort_s(NMParams p); - -/** - * Given the column index (starting from 1), returns an integer value that is represented by this column. Note that it is only possible to request this - * column as integer if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getInt_i(NMParams p); - -/** - * Given the column name (case insensitive), returns an integer value that is represented by this column. Note that it is only possible to request this - * column as integer if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retI receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getInt_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a long value that is represented by this column. Note that it is only possible to request this - * column as long if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retL receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getLong_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a long value that is represented by this column. Note that it is only possible to request this - * column as long if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retL receives the column value; if the value is SQL NULL, the value returned is 0. - */ -LB_API void lRS_getLong_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a float value that is represented by this column. Note that it is only possible to request this - * column as float if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getFloat_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a float value that is represented by this column. Note that it is only possible to request this - * column as float if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getFloat_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a double value that is represented by this column. Note that it is only possible to request this - * column as double if it was created with this precision. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getDouble_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a double value that is represented by this column. Note that it is only possible to request this - * column as double if it was created with this precision. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retD receives the column value; if the value is SQL NULL, the value returned is 0.0. - */ -LB_API void lRS_getDouble_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a char array that is represented by this column. Note that it is only possible to request this - * column as a char array if it was created as a string. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getChars_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a char array that is represented by this column. Note that it is only possible to request this - * column as a char array if it was created as a string. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getChars_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null - */ -LB_API void lRS_getString_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. This - * method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null - */ -LB_API void lRS_getString_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a byte (blob) array that is represented by this column. Note that it is only possible to request - * this column as a blob if it was created this way. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getBlob_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a byte array (blob) that is represented by this column. Note that it is only possible to request - * this column as a blob if it was created this way. This method is slightly slower then the method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getBlob_s(NMParams p); - -/** - * Starting from the current cursor position, it reads all result set rows that are being requested. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The number of rows to be fetched, or -1 for all. - * @param p->retO receives a matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column - * elements of the first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is null. - */ -LB_API void lRS_getStrings_i(NMParams p); - -/** - * Starting from the current cursor position, it reads all result set rows of the result set. first(), last(), - * prev() or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param p->obj[0] The result set. - * @param p->retO receives a matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column - * elements of the first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is null. - */ -LB_API void lRS_getStrings(NMParams p); - -/** - * Given the column index (starting from 1), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE or DATETIME). - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDate_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a Date value that is represented by this column. Note that it is only possible - * to request this column as a date if it was created this way (DATE or DATETIME). This method is slightly slower then the method that accepts a - * column index. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retO receives the column value; if the value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDate_s(NMParams p); - -/** - * Given the column index (starting from 1), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a date if it was created this way. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The colum index. - * @param p->retO receives the time of the DATETIME. If the DATETIME value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDateTime_i(NMParams p); - -/** - * Given the column name (case insensitive), returns a Time (correspondent to a DATETIME data type) value that is represented by this - * column. Note that it is only possible to request this column as a date if it was created this way. This method is slightly slower then the - * method that accepts a column index. - * - * @param p->obj[0] The result set. - * @param p->obj[0] The colum name. - * @param p->retO receives the time of the DATETIME. If the DATETIME value is SQL NULL, the value returned is null. - */ -LB_API void lRS_getDateTime_s(NMParams p); - -/** - * Places this result set cursor at the given absolute row. This is the absolute physical row of the table. This method is usually used to restore - * the row at a previous row got with the getRow() method. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The row to set the cursor. - * @param p->retI receives true whenever this method does not throw an exception. - */ -LB_API void lRS_absolute_i(NMParams p); - -/** - * Moves the cursor rows in distance. The value can be greater or lower than zero. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The distance to move the cursor. - * @param p->retI receives true whenever this method does not throw an exception. - */ -LB_API void lRS_relative_i(NMParams p); - -/** - * Returns the current physical row of the table where the cursor is. It must be used with absolute() method. - * - * @param p->obj[0] The result set. - * @param p->retI receives the current physical row of the table where the cursor is. - */ -LB_API void lRS_getRow(NMParams p); - -/** - * Sets the number of decimal places that the given column (starting from 1) will have when being converted to String. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column. - * @param p->i32[1] The number of decimal places. - * @throws DriverException If the column index is invalid, or the value for decimal places is invalid. - */ -LB_API void lRS_setDecimalPlaces_ii(NMParams p); - -/** - * Returns the number of rows of the result set. - * - * @param p->obj[0] The result set. - * @param p->retI receives the number of rows. - */ -LB_API void lRS_getRowCount(NMParams p); - -/** - * Given the column index (starting from 1), indicates if this column has a NULL. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives true if the value is SQL NULL; false, otherwise. - */ -LB_API void lRS_isNull_i(NMParams p); - -/** - * Transforms a ResultSet row in a string. - * - * @param p->obj[0] The result set. - * @param p->retO receives a whole current row of a ResultSet in a string with column data separated by tab. - */ -LB_API void lRS_rowToString(NMParams p); // juliana@270_30: added ResultSet.rowToString(). - -/** - * Given the column name (case insensitive), indicates if this column has a NULL. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param p->retI receives true if the value is SQL NULL; false, otherwise. - * @throws NullPointerException If the column name is null. - */ -LB_API void lRS_isNull_s(NMParams p); - -/** - * Gets the number of columns for this ResultSet. - * - * @param p->obj[0] The result set meta data. - * @param p->retI receives the number of columns for this ResultSet. - */ -LB_API void lRSMD_getColumnCount(NMParams p); - -/** - * Given the column index (starting at 1), returns the display size. For chars, it will return the number of chars defined; for primitive types, it - * will return the number of decimal places it needs to be displayed correctly. Returns 0 if an error occurs. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retI receives the display size or -1 if a problem occurs. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnDisplaySize_i(NMParams p); - -/** - * Given the column index (starting at 1), returns the column name. Note that if an alias is used to the column, the alias will be returned instead. - * If an error occurs, an empty string is returned. Note that LitebaseConnection 2.x tables must be recreated to be able to return this label - * information. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retO receives the name or alias of the column, which can be an empty string if an error occurs. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnLabel_i(NMParams p); - -/** - * Given the column index (starting at 1), returns the column type. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retI receives the column type, which can be: SHORT_TYPE, INT_TYPE, - * LONG_TYPE, FLOAT_TYPE, DOUBLE_TYPE, CHAR_TYPE, - * CHAR_NOCASE_TYPE, DATE_TYPE, DATETIME_TYPE, or BLOB_TYPE. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnType_i(NMParams p); - -/** - * Given the column index (starting at 1), returns the name of the column type. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index (starting at 1). - * @param p->retO receives the name of the column type, which can be: chars, short, int, - * long, float, double, date, datetime, - * blob, or null if an error occurs. - */ -LB_API void lRSMD_getColumnTypeName_i(NMParams p); - -/** - * Given the column index, (starting at 1) returns the name of the table it came from. - * - * @param p->obj[0] The result set meta data. - * @param p->i32[0] The column index. - * @param p->retO receives the name of the table it came from or null if the column index does not exist. - * @throws IllegalArgumentException If the column index is invalid. - */ -LB_API void lRSMD_getColumnTableName_i(NMParams p); - -/** - * Given the column name or alias, returns the name of the table it came from. - * - * @param p->obj[0] The result set meta data. - * @param p->obj[1] The column name. - * @param p->retO receives the name of the table it came from or null if the column name does not exist. - * @throws DriverException If the column was not found. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_getColumnTableName_s(NMParams p); - -/** - * Indicates if a column of the result set has default value. - * - * @param p->i32[0] The column index. - * @param p->retI receives true if the column has a default value; false, otherwise. - * @throws DriverException If the column does not have an underlining table. - */ -LB_API void lRSMD_hasDefaultValue_i(NMParams p); - -/** - * Indicates if a column of the result set has default value. - * - * @param p->obj[1] The column name. - * @param p->retI receives true if the column has a default value; false, otherwise. - * @throws DriverException If the column was not found or does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_hasDefaultValue_s(NMParams p); - -/** - * Indicates if a column of the result set is not null. - * - * @param p->i32[0] The column index. - * @param p->retI receives true if the column is not null; false, otherwise. - * @throws DriverException If the column does not have an underlining table. - */ -LB_API void lRSMD_isNotNull_i(NMParams p); - -/** - * Indicates if a column of the result set is not null. - * - * @param p->obj[1] The column name. - * @param p->retI receives true if the column is not null; false, otherwise. - * @throws DriverException If the column was not found or does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_isNotNull_s(NMParams p); - -/** - * Returns the primary key column indices of a table. - * - * @param p->obj[1] The table name. - * @param p->retO receives null if the given table does not have primary key or an array with the column indices of the primary key. - * @throws NullPointerException if the table name is null. - */ -LB_API void lRSMD_getPKColumnIndices_s(NMParams p); - -/** - * Returns the primary key column names of a table. - * - * @param p->obj[1] The table name. - * @param p->retO null if the given table does not have primary key or an array with the column names of the primary key. - * @throws NullPointerException if the table name is null. - */ -LB_API void lRSMD_getPKColumnNames_s(NMParams p); - -/** - * Returns the default value of a column. - * - * @param p->i32[0] The column index. - * @return p->retO receives the default value of the column as a string or null if there is no default value. - * @throws DriverException If the column index does not have an underlining table. - */ -LB_API void lRSMD_getDefaultValue_i(NMParams p); - -/** - * Returns the default value of a column. - * - * @param p->obj[1] The column name. - * @return p->retO receives the default value of the column as a string or null if there is no default value. - * @throws DriverException If the column name does not have an underlining table. - * @throws NullPointerException if the column name is null. - */ -LB_API void lRSMD_getDefaultValue_s(NMParams p); - -/** - * This method executes a prepared SQL query and returns its ResultSet. - * - * @param p->obj[0] The prepared statement. - * @param p->retO receives the ResultSet of the SQL statement. - * @throws DriverException If the statement to be execute is not a select, there are undefined parameters or the driver is closed. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_executeQuery(NMParams p); - -/** - * This method executes a SQL INSERT, UPDATE, or DELETE statement. SQL statements that return nothing such as - * SQL DDL statements can also be executed. - * - * @param p->obj[0] The prepared statement. - * @param p->retI receives the result is either the row count for INSERT, UPDATE, or DELETE statements; or 0 - * for SQL statements that return nothing. - * @throws DriverException If the query does not update the table or there are undefined parameters. - */ -LB_API void lPS_executeUpdate(NMParams p); - -/** - * This method sets the specified parameter from the given Java short value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i32[1] The value of the parameter. - */ -LB_API void lPS_setShort_is(NMParams p); - -/** - * This method sets the specified parameter from the given Java int value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i32[1] The value of the parameter. - */ -LB_API void lPS_setInt_ii(NMParams p); - -/** - * This method sets the specified parameter from the given Java long value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i64[0] The value of the parameter. - */ -LB_API void lPS_setLong_il(NMParams p); - -/** - * This method sets the specified parameter from the given Java float value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->dbl[0] The value of the parameter. - */ -LB_API void lPS_setFloat_id(NMParams p); - -/** - * This method sets the specified parameter from the given Java double value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->dbl[0] The value of the parameter. - */ -LB_API void lPS_setDouble_id(NMParams p); - -/** - * This method sets the specified parameter from the given Java String value. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. DO NOT SURROUND IT WITH '!. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_setString_is(NMParams p); - -/** - * This method sets the specified parameter from the given array of bytes as a blob. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - * @throws SQLParseException If the parameter to be set is in the where clause. - */ -LB_API void lPS_setBlob_iB(NMParams p); - -/** - * This method sets the specified parameter from the given Java Date value formated as "YYYY/MM/DD"
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, according - * to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, can generate - * a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date format - * parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_setDate_id(NMParams p); - -/** - * This method sets the specified parameter from the given Java DateTime value formated as "YYYY/MM/DD HH:MM:SS:ZZZ".
    - * IMPORTANT: The constructor new Date(string_date) must be used with care. Some devices can construct different dates, according - * to the device's date format. For example, the constructor new Date("12/09/2006"), depending on the device's date format, can generate - * a date like "12 of September of 2006" or "09 of December of 2006". To avoid this, use the constructor - * new Date(string_date, totalcross.sys.Settings.DATE_XXX) instead, where totalcross.sys.Settings.DATE_XXX is a date format - * parameter that must be one of the totalcross.sys.Settings.DATE_XXX constants. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - */ -LB_API void lPS_setDateTime_id(NMParams p); - -/** - * Formats the Time t into a string "YYYY/MM/DD HH:MM:SS:ZZZ" - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->obj[1] The value of the parameter. - * @throws OutOfMemoryError If a memory allocation fails. - */ -LB_API void lPS_setDateTime_it(NMParams p); - -/** - * Sets null in a given field. This can be used to set any column type as null. It must be just remembered that a parameter in a where clause can't - * be set to null. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set as null, starting from 0. - * @throws SQLParseException If the parameter to be set as null is in the where clause. - */ -LB_API void lPS_setNull_i(NMParams p); - -/** - * This method clears all of the input parameters that have been set on this statement. - * - * @param p->obj[0] The prepared statement. - */ -LB_API void lPS_clearParameters(NMParams p); - -/** - * Returns the sql used in this statement. If logging is disabled, returns the sql without the arguments. If logging is enabled, returns the real - * sql, filled with the arguments. - * - * @param p->obj[0] The prepared statement. - * @param p->obj[0] receives the sql used in this statement. - */ -LB_API void lPS_toString(NMParams p); - -/** - * Closes a prepared statement. - * - * @param p->obj[0] The prepared statement. - */ -LB_API void lPS_close(NMParams p); - -// juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - -/** - * Indicates if a prepared statement is valid or not: the driver is open and its SQL is in the hash table. - * - * @param p->obj[0] The prepared statement. - * @param p->retI receives true if the prepared statement is valid; false, otherwise. - */ -LB_API void lPS_isValid(NMParams p); - -#endif diff --git a/LitebaseSDK/src/native/NativeMethods.txt b/LitebaseSDK/src/native/NativeMethods.txt deleted file mode 100644 index 00765437d2..0000000000 --- a/LitebaseSDK/src/native/NativeMethods.txt +++ /dev/null @@ -1,116 +0,0 @@ -litebase/RowIterator|public native boolean next(); -litebase/RowIterator|public native boolean nextNotSynced(); -litebase/RowIterator|public native void setSynced(); -litebase/RowIterator|public native void setNotSynced(); -litebase/RowIterator|public native void close(); -litebase/RowIterator|public native void reset(); -litebase/RowIterator|public native short getShort(int column); -litebase/RowIterator|public native int getInt(int column); -litebase/RowIterator|public native long getLong(int column); -litebase/RowIterator|public native double getFloat(int column); -litebase/RowIterator|public native double getDouble(int column); -litebase/RowIterator|public native String getString(int column); -litebase/RowIterator|public native byte[] getBlob(int column); -litebase/RowIterator|public native totalcross.util.Date getDate(int column); -litebase/RowIterator|public native totalcross.sys.Time getDateTime(int column); -litebase/RowIterator|public native boolean isNull(int column) throws IllegalArgumentException; -litebase/LitebaseConnection|public static native litebase.LitebaseConnection privateGetInstance(); -litebase/LitebaseConnection|public static native litebase.LitebaseConnection privateGetInstance(String appCrid) throws DriverException, NullPointerException; -litebase/LitebaseConnection|public static native litebase.LitebaseConnection privateGetInstance(String appCrid, String params) throws DriverException, NullPointerException; -litebase/LitebaseConnection|public native String getSourcePath() throws IllegalStateException; -litebase/LitebaseConnection|public native void execute(String sql); -litebase/LitebaseConnection|public native int executeUpdate(String sql); -litebase/LitebaseConnection|public native litebase.ResultSet executeQuery(String sql); -litebase/LitebaseConnection|public native litebase.PrepareStatement prepareStatement(String sql) throws OutOfMemoryError; -litebase/LitebaseConnection|public native int getCurrentRowId(String tableName); -litebase/LitebaseConnection|public native int getRowCount(String tableName); -litebase/LitebaseConnection|public native void setRowInc(String tableName, int inc) throws IllegalArgumentException; -litebase/LitebaseConnection|public native boolean exists(String tableName) throws DriverException; -litebase/LitebaseConnection|public native void closeAll() throws IllegalStateException; -litebase/LitebaseConnection|public native int purge(String tableName) throws DriverException, OutOfMemoryError; -litebase/LitebaseConnection|public native int getRowCountDeleted(String tableName); -litebase/LitebaseConnection|public native litebase.RowIterator getRowIterator(String tableName); -litebase/LitebaseConnection|public static native totalcross.util.Logger privateGetLogger(); -litebase/LitebaseConnection|public static native void privateSetLogger(totalcross.util.Logger logger); -litebase/LitebaseConnection|public static native totalcross.util.Logger privateGetDefaultLogger() throws DriverException; -litebase/LitebaseConnection|public static native int privateDeleteLogFiles(); -litebase/LitebaseConnection|public static native litebase.LitebaseConnection privateProcessLogs(String []sql, String params, boolean isDebug) throws DriverException, NullPointerException, OutOfMemoryError; -litebase/LitebaseConnection|public native boolean recoverTable(String tableName) throws DriverException, OutOfMemoryError; -litebase/LitebaseConnection|public native void convert(String tableName) throws DriverException, OutOfMemoryError; -litebase/LitebaseConnection|public native int getSlot(); -litebase/LitebaseConnection|public boolean isOpen(String tableName) throws DriverException; -litebase/LitebaseConnection|public native static void dropDatabase(String crid, String sourcePath, int slot) throws DriverException, NullPointerException; -litebase/LitebaseConnection|public native boolean isTableProperlyClosed(String tableName) throws DriverException, NullPointerException; -litebase/LitebaseConnection|public native String[] listAllTables() throws DriverException, IllegalStateException, OutOfMemoryError; -litebase/LitebaseConnection|public native void encryptTables(String crid, String sourcePath, int slot); -litebase/LitebaseConnection|public native void decryptTables(String crid, String sourcePath, int slot); -litebase/ResultSet|public native litebase.ResultSetMetaData getResultSetMetaData(); -litebase/ResultSet|public native void close() throws IllegalStateException; -litebase/ResultSet|public native void beforeFirst() throws IllegalStateException; -litebase/ResultSet|public native void afterLast(); -litebase/ResultSet|public native boolean first(); -litebase/ResultSet|public native boolean last(); -litebase/ResultSet|public native boolean next(); -litebase/ResultSet|public native boolean prev(); -litebase/ResultSet|public native short getShort(int colIdx); -litebase/ResultSet|public native short getShort(String colName); -litebase/ResultSet|public native int getInt(int colIdx); -litebase/ResultSet|public native int getInt(String colName); -litebase/ResultSet|public native long getLong(int colIdx); -litebase/ResultSet|public native long getLong(String colName); -litebase/ResultSet|public native double getFloat(int colIdx); -litebase/ResultSet|public native double getFloat(String colName); -litebase/ResultSet|public native double getDouble(int colIdx); -litebase/ResultSet|public native double getDouble(String colName); -litebase/ResultSet|public native char[] getChars(int colIdx); -litebase/ResultSet|public native char[] getChars(String colName); -litebase/ResultSet|public native String getString(int colIdx); -litebase/ResultSet|public native String getString(String colName); -litebase/ResultSet|public native byte[] getBlob(int colIdx); -litebase/ResultSet|public native byte[] getBlob(String colName); -litebase/ResultSet|public native String[][] getStrings(int count); -litebase/ResultSet|public native String[][] getStrings(); -litebase/ResultSet|public native totalcross.util.Date getDate(int colIdx); -litebase/ResultSet|public native totalcross.util.Date getDate(String colName); -litebase/ResultSet|public native totalcross.sys.Time getDateTime(int colIdx); -litebase/ResultSet|public native totalcross.sys.Time getDateTime(String colName); -litebase/ResultSet|public native boolean absolute(int row); -litebase/ResultSet|public native boolean relative(int rows); -litebase/ResultSet|public native int getRow(); -litebase/ResultSet|public native void setDecimalPlaces(int col, int places) throws DriverException; -litebase/ResultSet|public native int getRowCount(); -litebase/ResultSet|public native boolean isNull(int col); -litebase/ResultSet|public native boolean isNull(String colName) throws NullPointerException; -litebase/ResultSet|public native String rowToString() throws DriverException; -litebase/ResultSetMetaData|public native int getColumnCount(); -litebase/ResultSetMetaData|public native int getColumnDisplaySize(int column) throws IllegalArgumentException; -litebase/ResultSetMetaData|public native String getColumnLabel(int column) throws IllegalArgumentException; -litebase/ResultSetMetaData|public native int getColumnType(int column) throws IllegalArgumentException; -litebase/ResultSetMetaData|public native String getColumnTypeName(int column); -litebase/ResultSetMetaData|public native String getColumnTableName(int columnIdx) throws IllegalArgumentException; -litebase/ResultSetMetaData|public native String getColumnTableName(String columnName) throws DriverException, NullPointerException; -litebase/ResultSetMetaData|public native boolean hasDefaultValue(int columnIndex) throws DriverException; -litebase/ResultSetMetaData|public native boolean hasDefaultValue(String columnName) throws DriverException, NullPointerException; -litebase/ResultSetMetaData|public native boolean isNotNull(int columnIndex) throws DriverException; -litebase/ResultSetMetaData|public native boolean isNotNull(String columnName) throws DriverException, NullPointerException; -litebase/ResultSetMetaData|public native byte[] getPKColumnIndices(String tableName) throws NullPointerException; -litebase/ResultSetMetaData|public native String[] getPKColumnNames(String tableName) throws NullPointerException; -litebase/ResultSetMetaData|public native String getDefaultValue(int columnIndex) throws DriverException; -litebase/ResultSetMetaData|public native String getDefaultValue(String columnName) throws DriverException, NullPointerException; -litebase/PreparedStatement|public native litebase.ResultSet executeQuery() throws DriverException, OutOfMemoryError; -litebase/PreparedStatement|public native int executeUpdate() throws DriverException; -litebase/PreparedStatement|public native void setShort(int index, short value); -litebase/PreparedStatement|public native void setInt(int index, int value); -litebase/PreparedStatement|public native void setLong(int index, long value); -litebase/PreparedStatement|public native void setFloat(int index, double value); -litebase/PreparedStatement|public native void setDouble(int index, double value); -litebase/PreparedStatement|public native void setString(int index, String value) throws OutOfMemoryError; -litebase/PreparedStatement|public native void setBlob(int index, byte []value) throws SQLParseException; -litebase/PreparedStatement|public native void setDate(int index, totalcross.util.Date) throws OutOfMemoryError; -litebase/PreparedStatement|public native void setDateTime(int index, totalcross.util.Date); -litebase/PreparedStatement|public native void setDateTime(int index, totalcross.sys.Time) throws OutOfMemoryError; -litebase/PreparedStatement|public native void setNull(int index) throws SQLParseException; -litebase/PreparedStatement|public native void clearParameters(); -litebase/PreparedStatement|public native String toString(); -litebase/PreparedStatement|public native void close(); -litebase/PreparedStatement|public native boolean isValid(); diff --git a/LitebaseSDK/src/native/NativeMethodsPrototypes.txt b/LitebaseSDK/src/native/NativeMethodsPrototypes.txt deleted file mode 100644 index af58f4e614..0000000000 --- a/LitebaseSDK/src/native/NativeMethodsPrototypes.txt +++ /dev/null @@ -1,574 +0,0 @@ - -TC_API void lRI_next(NMParams p); -TC_API void lRI_nextNotSynced(NMParams p); -TC_API void lRI_setSynced(NMParams p); -TC_API void lRI_setNotSynced(NMParams p); -TC_API void lRI_close(NMParams p); -TC_API void lRI_reset(NMParams p); -TC_API void lRI_getShort_i(NMParams p); -TC_API void lRI_getInt_i(NMParams p); -TC_API void lRI_getLong_i(NMParams p); -TC_API void lRI_getFloat_i(NMParams p); -TC_API void lRI_getDouble_i(NMParams p); -TC_API void lRI_getString_i(NMParams p); -TC_API void lRI_getBlob_i(NMParams p); -TC_API void lRI_getDate_i(NMParams p); -TC_API void lRI_getDateTime_i(NMParams p); -TC_API void lRI_isNull_i(NMParams p); -TC_API void lLC_privateGetInstance(NMParams p); -TC_API void lLC_privateGetInstance_s(NMParams p); -TC_API void lLC_privateGetInstance_ss(NMParams p); -TC_API void lLC_getSourcePath(NMParams p); -TC_API void lLC_execute_s(NMParams p); -TC_API void lLC_executeUpdate_s(NMParams p); -TC_API void lLC_executeQuery_s(NMParams p); -TC_API void lLC_prepareStatement_s(NMParams p); -TC_API void lLC_getCurrentRowId_s(NMParams p); -TC_API void lLC_getRowCount_s(NMParams p); -TC_API void lLC_setRowInc_si(NMParams p); -TC_API void lLC_exists_s(NMParams p); -TC_API void lLC_closeAll(NMParams p); -TC_API void lLC_purge_s(NMParams p); -TC_API void lLC_getRowCountDeleted_s(NMParams p); -TC_API void lLC_getRowIterator_s(NMParams p); -TC_API void lLC_privateGetLogger(NMParams p); -TC_API void lLC_privateSetLogger_l(NMParams p); -TC_API void lLC_privateGetDefaultLogger(NMParams p); -TC_API void lLC_privateDeleteLogFiles(NMParams p); -TC_API void lLC_privateProcessLogs_Ssb(NMParams p); -TC_API void lLC_recoverTable_s(NMParams p); -TC_API void lLC_convert_s(NMParams p); -TC_API void lLC_getSlot(NMParams p); -TC_API void lLC_isOpen_s(NMParams p); -TC_API void lLC_dropDatabase_ssi(NMParams p); -TC_API void lLC_isTableProperlyClosed_s(NMParams p); -TC_API void lLC_listAllTables(NMParams p); -TC_API void lLC_encryptTables_ssi(NMParams p); -TC_API void lLC_decryptTables_ssi(NMParams p); -TC_API void lRS_getResultSetMetaData(NMParams p); -TC_API void lRS_close(NMParams p); -TC_API void lRS_beforeFirst(NMParams p); -TC_API void lRS_afterLast(NMParams p); -TC_API void lRS_first(NMParams p); -TC_API void lRS_last(NMParams p); -TC_API void lRS_next(NMParams p); -TC_API void lRS_prev(NMParams p); -TC_API void lRS_getShort_i(NMParams p); -TC_API void lRS_getShort_s(NMParams p); -TC_API void lRS_getInt_i(NMParams p); -TC_API void lRS_getInt_s(NMParams p); -TC_API void lRS_getLong_i(NMParams p); -TC_API void lRS_getLong_s(NMParams p); -TC_API void lRS_getFloat_i(NMParams p); -TC_API void lRS_getFloat_s(NMParams p); -TC_API void lRS_getDouble_i(NMParams p); -TC_API void lRS_getDouble_s(NMParams p); -TC_API void lRS_getChars_i(NMParams p); -TC_API void lRS_getChars_s(NMParams p); -TC_API void lRS_getString_i(NMParams p); -TC_API void lRS_getString_s(NMParams p); -TC_API void lRS_getBlob_i(NMParams p); -TC_API void lRS_getBlob_s(NMParams p); -TC_API void lRS_getStrings_i(NMParams p); -TC_API void lRS_getStrings(NMParams p); -TC_API void lRS_getDate_i(NMParams p); -TC_API void lRS_getDate_s(NMParams p); -TC_API void lRS_getDateTime_i(NMParams p); -TC_API void lRS_getDateTime_s(NMParams p); -TC_API void lRS_absolute_i(NMParams p); -TC_API void lRS_relative_i(NMParams p); -TC_API void lRS_getRow(NMParams p); -TC_API void lRS_setDecimalPlaces_ii(NMParams p); -TC_API void lRS_getRowCount(NMParams p); -TC_API void lRS_isNull_i(NMParams p); -TC_API void lRS_isNull_s(NMParams p); -TC_API void lRS_rowToString(NMParams p); -TC_API void lRSMD_getColumnCount(NMParams p); -TC_API void lRSMD_getColumnDisplaySize_i(NMParams p); -TC_API void lRSMD_getColumnLabel_i(NMParams p); -TC_API void lRSMD_getColumnType_i(NMParams p); -TC_API void lRSMD_getColumnTypeName_i(NMParams p); -TC_API void lRSMD_getColumnTableName_i(NMParams p); -TC_API void lRSMD_getColumnTableName_s(NMParams p); -TC_API void lRSMD_hasDefaultValue_i(NMParams p); -TC_API void lRSMD_hasDefaultValue_s(NMParams p); -TC_API void lRSMD_isNotNull_i(NMParams p); -TC_API void lRSMD_isNotNull_s(NMParams p); -TC_API void lRSMD_getPKColumnIndices_s(NMParams p); -TC_API void lRSMD_getPKColumnNames_s(NMParams p); -TC_API void lRSMD_getDefaultValue_i(NMParams p); -TC_API void lRSMD_getDefaultValue_s(NMParams p); -TC_API void lPS_executeQuery(NMParams p); -TC_API void lPS_executeUpdate(NMParams p); -TC_API void lPS_setShort_is(NMParams p); -TC_API void lPS_setInt_ii(NMParams p); -TC_API void lPS_setLong_il(NMParams p); -TC_API void lPS_setFloat_id(NMParams p); -TC_API void lPS_setDouble_id(NMParams p); -TC_API void lPS_setString_is(NMParams p); -TC_API void lPS_setBlob_iB(NMParams p); -TC_API void lPS_setDate_id(NMParams p); -TC_API void lPS_setDateTime_id(NMParams p); -TC_API void lPS_setDateTime_it(NMParams p); -TC_API void lPS_setNull_i(NMParams p); -TC_API void lPS_clearParameters(NMParams p); -TC_API void lPS_toString(NMParams p); -TC_API void lPS_close(NMParams p); -TC_API void lPS_isValid(NMParams p); - -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_next(NMParams p) // litebase/RowIterator public native boolean next(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_nextNotSynced(NMParams p) // litebase/RowIterator public native boolean nextNotSynced(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_setSynced(NMParams p) // litebase/RowIterator public native void setSynced(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_close(NMParams p) // litebase/RowIterator public native void close(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_reset(NMParams p) // litebase/RowIterator public native void reset(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getShort_i(NMParams p) // litebase/RowIterator public native short getShort(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getInt_i(NMParams p) // litebase/RowIterator public native int getInt(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getLong_i(NMParams p) // litebase/RowIterator public native long getLong(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getFloat_i(NMParams p) // litebase/RowIterator public native double getFloat(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getDouble_i(NMParams p) // litebase/RowIterator public native double getDouble(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getString_i(NMParams p) // litebase/RowIterator public native String getString(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getBlob_i(NMParams p) // litebase/RowIterator public native byte[] getBlob(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getDate_i(NMParams p) // litebase/RowIterator public native totalcross.util.Date getDate(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_getDateTime_i(NMParams p) // litebase/RowIterator public native totalcross.sys.Time getDateTime(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRI_isNull_i(NMParams p) // litebase/RowIterator public native boolean isNull(int column) throws IllegalArgumentException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateGetInstance(NMParams p) // litebase/LitebaseConnection public static native litebase.LitebaseConnection privateGetInstance(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateGetInstance_s(NMParams p) // litebase/LitebaseConnection public static native litebase.LitebaseConnection privateGetInstance(String appCrid) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateGetInstance_ss(NMParams p) // litebase/LitebaseConnection public static native litebase.LitebaseConnection privateGetInstance(String appCrid, String params) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_getSourcePath(NMParams p) // litebase/LitebaseConnection public native String getSourcePath() throws IllegalStateException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_execute_s(NMParams p) // litebase/LitebaseConnection public native void execute(String sql); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_executeUpdate_s(NMParams p) // litebase/LitebaseConnection public native int executeUpdate(String sql); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_executeQuery_s(NMParams p) // litebase/LitebaseConnection public native litebase.ResultSet executeQuery(String sql); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_prepareStatement_s(NMParams p) // litebase/LitebaseConnection public native litebase.PrepareStatement prepareStatement(String sql) throws OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_getCurrentRowId_s(NMParams p) // litebase/LitebaseConnection public native int getCurrentRowId(String tableName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_getRowCount_s(NMParams p) // litebase/LitebaseConnection public native int getRowCount(String tableName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_setRowInc_si(NMParams p) // litebase/LitebaseConnection public native void setRowInc(String tableName, int inc) throws IllegalArgumentException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_exists_s(NMParams p) // litebase/LitebaseConnection public native boolean exists(String tableName) throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_closeAll(NMParams p) // litebase/LitebaseConnection public native void closeAll() throws IllegalStateException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_purge_s(NMParams p) // litebase/LitebaseConnection public native int purge(String tableName) throws DriverException, OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_getRowCountDeleted_s(NMParams p) // litebase/LitebaseConnection public native int getRowCountDeleted(String tableName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_getRowIterator_s(NMParams p) // litebase/LitebaseConnection public native litebase.RowIterator getRowIterator(String tableName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateGetLogger(NMParams p) // litebase/LitebaseConnection public static native totalcross.util.Logger privateGetLogger(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateSetLogger_l(NMParams p) // litebase/LitebaseConnection public static native void privateSetLogger(totalcross.util.Logger logger); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateGetDefaultLogger(NMParams p) // litebase/LitebaseConnection public static native totalcross.util.Logger privateGetDefaultLogger() throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateDeleteLogFiles(NMParams p) // litebase/LitebaseConnection public static native int privateDeleteLogFiles(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_privateProcessLogs_Ssb(NMParams p) // litebase/LitebaseConnection public static native litebase.LitebaseConnection privateProcessLogs(String []sql, String params, boolean isDebug) throws DriverException, NullPointerException, OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_recoverTable_s(NMParams p) // litebase/LitebaseConnection public native boolean recoverTable(String tableName) throws DriverException, OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_convert_s(NMParams p) // litebase/LitebaseConnection public native void convert(String tableName) throws DriverException, OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_getSlot(NMParams p) // litebase/LitebaseConnection public native int getSlot(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_isOpen_s(NMParams p) // litebase/LitebaseConnection public boolean isOpen(String tableName) throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_dropDatabase_ssi(NMParams p) // litebase/LitebaseConnection public native static void dropDatabase(String crid, String sourcePath, int slot) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_isTableProperlyClosed_s(NMParams p) // litebase/LitebaseConnection public native boolean isTableProperlyClosed(String tableName) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_listAllTables(NMParams p) // litebase/LitebaseConnection public native String[] listAllTables() throws DriverException, IllegalStateException, OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_encryptTables_ssi(NMParams p) // litebase/LitebaseConnection public native void encryptTables(String crid, String sourcePath, int slot); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lLC_decryptTables_ssi(NMParams p) // litebase/LitebaseConnection public native void decryptTables(String crid, String sourcePath, int slot); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getResultSetMetaData(NMParams p) // litebase/ResultSet public native litebase.ResultSetMetaData getResultSetMetaData(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_close(NMParams p) // litebase/ResultSet public native void close() throws IllegalStateException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_beforeFirst(NMParams p) // litebase/ResultSet public native void beforeFirst() throws IllegalStateException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_afterLast(NMParams p) // litebase/ResultSet public native void afterLast(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_first(NMParams p) // litebase/ResultSet public native boolean first(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_last(NMParams p) // litebase/ResultSet public native boolean last(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_next(NMParams p) // litebase/ResultSet public native boolean next(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_prev(NMParams p) // litebase/ResultSet public native boolean prev(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getShort_i(NMParams p) // litebase/ResultSet public native short getShort(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getShort_s(NMParams p) // litebase/ResultSet public native short getShort(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getInt_i(NMParams p) // litebase/ResultSet public native int getInt(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getInt_s(NMParams p) // litebase/ResultSet public native int getInt(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getLong_i(NMParams p) // litebase/ResultSet public native long getLong(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getLong_s(NMParams p) // litebase/ResultSet public native long getLong(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getFloat_i(NMParams p) // litebase/ResultSet public native double getFloat(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getFloat_s(NMParams p) // litebase/ResultSet public native double getFloat(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getDouble_i(NMParams p) // litebase/ResultSet public native double getDouble(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getDouble_s(NMParams p) // litebase/ResultSet public native double getDouble(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getChars_i(NMParams p) // litebase/ResultSet public native char[] getChars(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getChars_s(NMParams p) // litebase/ResultSet public native char[] getChars(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getString_i(NMParams p) // litebase/ResultSet public native String getString(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getString_s(NMParams p) // litebase/ResultSet public native String getString(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getBlob_i(NMParams p) // litebase/ResultSet public native byte[] getBlob(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getBlob_s(NMParams p) // litebase/ResultSet public native byte[] getBlob(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getStrings_i(NMParams p) // litebase/ResultSet public native String[][] getStrings(int count); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getStrings(NMParams p) // litebase/ResultSet public native String[][] getStrings(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getDate_i(NMParams p) // litebase/ResultSet public native totalcross.util.Date getDate(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getDate_s(NMParams p) // litebase/ResultSet public native totalcross.util.Date getDate(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getDateTime_i(NMParams p) // litebase/ResultSet public native totalcross.sys.Time getDateTime(int colIdx); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getDateTime_s(NMParams p) // litebase/ResultSet public native totalcross.sys.Time getDateTime(String colName); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_absolute_i(NMParams p) // litebase/ResultSet public native boolean absolute(int row); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_relative_i(NMParams p) // litebase/ResultSet public native boolean relative(int rows); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getRow(NMParams p) // litebase/ResultSet public native int getRow(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_setDecimalPlaces_ii(NMParams p) // litebase/ResultSet public native void setDecimalPlaces(int col, int places) throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_getRowCount(NMParams p) // litebase/ResultSet public native int getRowCount(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_isNull_i(NMParams p) // litebase/ResultSet public native boolean isNull(int col); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRS_isNull_s(NMParams p) // litebase/ResultSet public native boolean isNull(String colName) throws NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnCount(NMParams p) // litebase/ResultSetMetaData public native int getColumnCount(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnDisplaySize_i(NMParams p) // litebase/ResultSetMetaData public native int getColumnDisplaySize(int column) throws IllegalArgumentException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnLabel_i(NMParams p) // litebase/ResultSetMetaData public native String getColumnLabel(int column) throws IllegalArgumentException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnType_i(NMParams p) // litebase/ResultSetMetaData public native int getColumnType(int column) throws IllegalArgumentException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnTypeName_i(NMParams p) // litebase/ResultSetMetaData public native String getColumnTypeName(int column); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnTableName_i(NMParams p) // litebase/ResultSetMetaData public native String getColumnTableName(int columnIdx) throws IllegalArgumentException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getColumnTableName_s(NMParams p) // litebase/ResultSetMetaData public native String getColumnTableName(String columnName) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_hasDefaultValue_i(NMParams p) // litebase/ResultSetMetaData public native boolean hasDefaultValue(int columnIndex) throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_hasDefaultValue_s(NMParams p) // litebase/ResultSetMetaData public native boolean hasDefaultValue(String columnName) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_isNotNull_i(NMParams p) // litebase/ResultSetMetaData public native boolean isNotNull(int columnIndex) throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_isNotNull_s(NMParams p) // litebase/ResultSetMetaData public native boolean isNotNull(String columnName) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getPKColumnIndices_s(NMParams p) // litebase/ResultSetMetaData public native byte[] getPKColumnIndices(String tableName) throws NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getPKColumnNames_s(NMParams p) // litebase/ResultSetMetaData public native String[] getPKColumnNames(String tableName) throws NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getDefaultValue_i(NMParams p) // litebase/ResultSetMetaData public native String getDefaultValue(int columnIndex) throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lRSMD_getDefaultValue_s(NMParams p) // litebase/ResultSetMetaData public native String getDefaultValue(String columnName) throws DriverException, NullPointerException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_executeQuery(NMParams p) // litebase/PreparedStatement public native litebase.ResultSet executeQuery() throws DriverException, OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_executeUpdate(NMParams p) // litebase/PreparedStatement public native int executeUpdate() throws DriverException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setShort_is(NMParams p) // litebase/PreparedStatement public native void setShort(int index, short value); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setInt_ii(NMParams p) // litebase/PreparedStatement public native void setInt(int index, int value); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setLong_il(NMParams p) // litebase/PreparedStatement public native void setLong(int index, long value); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setFloat_id(NMParams p) // litebase/PreparedStatement public native void setFloat(int index, double value); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setDouble_id(NMParams p) // litebase/PreparedStatement public native void setDouble(int index, double value); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setString_is(NMParams p) // litebase/PreparedStatement public native void setString(int index, String value) throws OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setBlob_iB(NMParams p) // litebase/PreparedStatement public native void setBlob(int index, byte []value) throws SQLParseException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setDate_id(NMParams p) // litebase/PreparedStatement public native void setDate(int index, totalcross.util.Date) throws OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setDateTime_id(NMParams p) // litebase/PreparedStatement public native void setDateTime(int index, totalcross.util.Date); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setDateTime_it(NMParams p) // litebase/PreparedStatement public native void setDateTime(int index, totalcross.sys.Time) throws OutOfMemoryError; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_setNull_i(NMParams p) // litebase/PreparedStatement public native void setNull(int index) throws SQLParseException; -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_clearParameters(NMParams p) // litebase/PreparedStatement public native void clearParameters(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_toString(NMParams p) // litebase/PreparedStatement public native String toString(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_close(NMParams p) // litebase/PreparedStatement public native void close(); -{ -} -////////////////////////////////////////////////////////////////////////// -TC_API void lPS_isValid(NMParams p) // litebase/PreparedStatement public native boolean isValid(); -{ -} diff --git a/LitebaseSDK/src/native/Node.c b/LitebaseSDK/src/native/Node.c deleted file mode 100644 index 328c12cb05..0000000000 --- a/LitebaseSDK/src/native/Node.c +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@253_5: removed .idr files from all indices and changed its format. -// juliana@253_6: the maximum number of keys of a index was duplicated. -/** - * Defines functions to manipulate a B-Tree. It is used to store the table indices. It has some improvements for both memory usage, disk space, and - * speed, targeting the creation of indices, where the table's record is far greater than the index record. - */ - -#include "Node.h" - -/** - * Creates a new node for an index. - * - * @param index The index of the node to be created. - * @return The node created. - */ -Node* createNode(Index* index) -{ - TRACE("createNode") - Heap heap = index->heap; - Node* node = (Node*)TC_heapAlloc(heap, sizeof(Node)); - int32* colSizes = index->colSizes; - int32 i = (node->index = index)->btreeMaxNodes, - j, - numberColumns = index->numberColumns; - Key* keys = node->keys = (Key*)TC_heapAlloc(heap, sizeof(Key) * i); - Key* key; - - node->idx = -1; - node->children = (uint16*)TC_heapAlloc(heap, (i + 1) << 1); - - while (--i >= 0) - { - key = &keys[i]; - key->index = index; - key->keys = (SQLValue*)TC_heapAlloc(heap, numberColumns * sizeof(SQLValue)); - j = numberColumns; - while (--j >= 0) - if (colSizes[j]) - key->keys[j].asChars = (JCharP)TC_heapAlloc(heap, (colSizes[j] << 1) + 2); - } - return node; -} - -/** - * Loads a node. - * - * @param context The thread context where the function is being executed. - * @param node A pointer to the node being loaded. - * @return false if an error occurs; true, otherwise. - */ -bool nodeLoad(Context context, Node* node) -{ - TRACE("nodeLoad") - Index* index = node->index; - XFile* fnodes = &index->fnodes; - uint8* dataStream = index->basbuf; - uint16* children = node->children; - Key* keys = node->keys; - int32 i = index->nodeRecSize, - n = 0; - - // Reads all the record at once. - nfSetPos(fnodes, node->idx * i); - if (!nfReadBytes(context, fnodes, dataStream, i)) - return false; - - // Loads the keys. - xmove2(&n, dataStream); - dataStream += 2; - i = -1; - while (++ i < n) - dataStream = keyLoad(&keys[i], dataStream); - - xmemmove(children, dataStream, ((node->size = n) + 1) << 1); // Loads the node children. - - // juliana@202_3: Solved a bug that could cause a GPF when using composed indices. - xmemset(&children[n + 1], LEAF, (index->btreeMaxNodes - n) << 1); // Fills the non-used indexes with TERMINAL. - - node->isDirty = false; - return true; -} - -/** - * Saves a dirty key. - * - * @param context The thread context where the function is being executed. - * @param node The node being saved. - * @param currPos The current position in the file where the key should be saved. - * @return false if an error occurs; true, otherwise. - */ -bool nodeSaveDirtyKey(Context context, Node* node, int32 currPos) -{ - TRACE("nodeSaveDirtyKey") - Index* index = node->index; - XFile* fnodes = &index->fnodes; - - // Positions the file pointer at the insert position. - nfSetPos(fnodes, node->idx * index->nodeRecSize + 2 + index->keyRecSize * currPos + (index->keyRecSize - VALREC_SIZE)); - - xmove4(index->basbuf, &node->keys[currPos].record); - return nfWriteBytes(context, fnodes, index->basbuf, 4); -} - -/** - * Saves a node. - * - * @param context The thread context where the function is being executed. - * @param node The node being saved. - * @param isNew Indicates if it is a new node, not saved yet. - * @param left The left child. - * @param right The right child. - * @return The position of this node. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the index gets too large. - */ -int32 nodeSave(Context context, Node* node, bool isNew, int32 left, int32 right) -{ - TRACE("nodeSave") - Index* index = node->index; - XFile* fnodes = &index->fnodes; - Key* keys = node->keys; - uint8* dataStream = index->basbuf; - int32 i = right - left, - idx = node->idx, - nodeRecSize = index->nodeRecSize; - - if (isNew) - { - if ((idx = index->nodeCount++) >= MAX_IDX) // The index got too large! - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INDEX_LARGE)); - return -1; - } - - if (index->isWriteDelayed) // Grows more than 1 record per time. - { - if (idx * nodeRecSize == fnodes->size && !nfGrowTo(context, fnodes, (idx + RECGROWSIZE) * nodeRecSize)) - return -1; - } - else if (!nfGrowTo(context, fnodes, (idx + 1) * nodeRecSize)) // Opens space for the node. - return -1; - } - - nfSetPos(fnodes, idx * nodeRecSize); // Rewinds to insert position. - xmove2(dataStream, &i); - dataStream += 2; - - // Saves the keys. - i = left - 1; - while (++i < right) - dataStream = keySave(&keys[i], dataStream); - - // juliana@225_2: corrected a possible index corruption when updating each node children. - // Saves the children; - xmemmove(dataStream, &node->children[left], i = ((right - left + 1) << 1)); - dataStream += i; - -// juliana@230_35: now the first level nodes of a b-tree index will be loaded in memory. - if (isNew && idx > 0 && idx <= index->btreeMaxNodes) - { - Node** firstLevel = index->firstLevel; - Node* newNode = firstLevel[idx - 1]; - Key* newKeys; - - if (!newNode) - newNode = firstLevel[idx - 1] = createNode(index); - - newKeys = newNode->keys; - newNode->idx = idx; - xmemmove(newNode->children, &node->children[left], i); - i = newNode->size = right - left; - while (--i >= 0) - keySetFromKey(&newKeys[i], &keys[i + left]); - newNode->isDirty = false; - } - - xmemzero(dataStream, nodeRecSize - (dataStream - index->basbuf)); // Fills the rest with zeros. - if (!nfWriteBytes(context, fnodes, index->basbuf, nodeRecSize)) - return -1; - - node->isDirty = false; - return idx; -} - -/** - * Constructs a B-Tree node with at most k keys, initially with one element, item, and two children: left and right. - * - * @param node The node being saved. - * @param key The key to be saved. - * @param left The left child. - * @param right The right child. - */ -void nodeSet(Node* node, Key* key, int32 left, int32 right) -{ - TRACE("nodeSet") - node->size = 1; - keySetFromKey(node->keys, key); - node->children[0] = left; - node->children[1] = right; -} - -/** - * Returns the index of the leftmost element of this node that is not less than item, using a binary search. - * - * @param context The thread context where the function is being executed. - * @param node The node being searched. - * @param key The key to be found. - * @param isInsert Indicates that the function is called by indexInsert() - * @return The position of the key. - */ -int32 nodeFindIn(Context context, Node* node, Key* key, bool isInsert) // juliana@201_3 -{ - TRACE("nodeFindIn") - Index* index = node->index; - PlainDB* plainDB = &index->table->db; - Key* keys = node->keys; - int32 right = node->size - 1, - middle, - comp, - numberColumns = index->numberColumns, - - // juliana@201_3: If the insertion is ordered, the position being seached is the last. - left = (isInsert && index->isOrdered && right > 0)? right : 0; - - while (left <= right) - { - if (!(comp = keyCompareTo(context, key, &keys[middle = (left + right) >> 1], numberColumns, plainDB))) - return middle; - else if (comp < 0) - right = middle - 1; - else - left = middle + 1; - } - return left; -} - -/** - * Inserts element item, with left and right children at the right position in this node. - * - * @param context The thread context where the function is being executed. - * @param node The node where a key will be inserted. - * @param key The key to be saved. - * @param leftChild The left child of the node. - * @param rightChild The right child of the node. - * @param insPos The position where to insert the key. - * @return false if an error occurs; true, otherwise. - */ -bool nodeInsert(Context context, Node* node, Key* key, int32 leftChild, int32 rightChild, int32 insPos) -{ - TRACE("nodeInsert") - Key* keys = node->keys; - uint16* children = node->children; - int32 i = node->size - insPos; - if (i > 0) - { - xmemmove(&children[insPos + 2], &children[insPos + 1], i << 1); - while (--i >= 0) - keySetFromKey(&keys[insPos + i + 1], &keys[insPos + i]); - } - - keySetFromKey(&keys[insPos], key); - children[insPos] = leftChild; - children[insPos + 1] = rightChild; - node->size++; - if (node->index->isWriteDelayed) // Only saves the key if it is not to be saved later. - node->isDirty = true; - else - return nodeSave(context, node, false, 0, node->size) >= 0; - return true; -} - -/** - * Sets the flag that indicates if the not should have its write process delayed or not. - * - * @param context The thread context where the function is being executed. - * @param node The node whose flag will be updated. - * @param delayed The new value of the flag. - * @return false if an error occurs; true, otherwise. - */ -bool nodeSetWriteDelayed(Context context, Node* node, bool delayed) -{ - TRACE(delayed ? "nodeSetWriteDelayed on" : "nodeSetWriteDelayed off") - - // Before changing the flag, flushs the node. - if (node && node->index->isWriteDelayed && node->isDirty && !delayed && nodeSave(context, node, false, 0, node->size) < 0) - return false; - - return true; -} diff --git a/LitebaseSDK/src/native/Node.h b/LitebaseSDK/src/native/Node.h deleted file mode 100644 index 4cc3e3d4d2..0000000000 --- a/LitebaseSDK/src/native/Node.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions to manipulate a B-Tree. It is used to store the table indices. It has some improvements for both memory usage, disk space, and - * speed, targeting the creation of indices, where the table's record is far greater than the index record. - */ - -#ifndef LITEBASE_NODE_H -#define LITEBASE_NODE_H - -#include "Litebase.h" - -/** - * Creates a new node for an index. - * - * @param index The index of the node to be created. - * @return The node created. - */ -Node* createNode(Index* index); - -/** - * Loads a node. - * - * @param context The thread context where the function is being executed. - * @param node The node being loaded. - * @return false if an error occurs; true, otherwise. - */ -bool nodeLoad(Context context, Node* node); - -/** - * Saves a dirty key. - * - * @param context The thread context where the function is being executed. - * @param node The node being saved. - * @param currPos The current position in the file where the key should be saved. - * @return false if an error occurs; true, otherwise. - */ -bool nodeSaveDirtyKey(Context context, Node* node, int32 currPos); - -/** - * Saves a node. - * - * @param context The thread context where the function is being executed. - * @param node The node being saved. - * @param isNew Indicates if it is a new node, not saved yet. - * @param left The left child. - * @param right The right child. - * @return The position of this node. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the index gets too large. - */ -int32 nodeSave(Context context, Node* node, bool isNew, int32 left, int32 right); - -/** - * Constructs a B-Tree node with at most k keys, initially with one element, item, and two children: left and right. - * - * @param node The node being saved. - * @param key The key to be saved. - * @param left The left child. - * @param right The right child. - */ -void nodeSet(Node* node, Key* key, int32 left, int32 right); - -/** - * Returns the index of the leftmost element of this node that is not less than item, using a binary search. - * - * @param context The thread context where the function is being executed. - * @param node The node being searched. - * @param key The key to be found. - * @param isInsert Indicates that the function is called by indexInsert() - * @return The position of the key. - */ -int32 nodeFindIn(Context context, Node* node, Key* key, bool isInsert); - -/** - * Inserts element item, with left and right children at the right position in this node. - * - * @param context The thread context where the function is being executed. - * @param node The node where a key will be inserted. - * @param key The key to be saved. - * @param leftChild The left child of the node. - * @param rightChild The right child of the node. - * @param insPos The position where to insert the key. - * @return false if an error occurs; true, otherwise. - */ -bool nodeInsert(Context context, Node* node, Key* key, int32 leftChild, int32 rightChild, int32 insPos); - -/** - * Sets the flag that indicates if the not should have its write process delayed or not. - * - * @param context The thread context where the function is being executed. - * @param node The node whose flag will be updated. - * @param delayed The new value of the flag. - * @return false if an error occurs; true, otherwise. - */ -bool nodeSetWriteDelayed(Context context, Node* node, bool delayed); - -#endif diff --git a/LitebaseSDK/src/native/NormalFile.c b/LitebaseSDK/src/native/NormalFile.c deleted file mode 100644 index 6f504f73f7..0000000000 --- a/LitebaseSDK/src/native/NormalFile.c +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines functions for a normal file, ie, a file that is stored on disk. - */ - -#include "NormalFile.h" - -/** - * Creates a disk file to store tables. - * - * @param context The thread context where the function is being executed. - * @param name The name of the file. - * @param isCreation Indicates if the file must be created or just open. - * @param useCrypto Indicates if the table uses cryptography. - * @param sourcePath The path where the file will be created. - * @param xFile A pointer to the normal file structure. - * @param cacheSize The cache size to be used. -1 should be passed if the default value is to be used. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the file cannot be open. - * @throws OutOfMemoryError If there is not enough memory to create the normal file cache. - */ -bool nfCreateFile(Context context, CharP name, bool isCreation, bool useCrypto, TCHARP sourcePath, XFile* xFile, int32 cacheSize) -{ - TRACE("nfCreateFile") - TCHAR buffer[MAX_PATHNAME]; - uint32 ret; - - xmemzero(xFile, sizeof(XFile)); - fileInvalidate(xFile->file); - - // juliana@252_3: corrected a possible crash if the path had more than 255 characteres. - if (xstrlen(name) + tcslen(sourcePath) + 1 > MAX_PATHNAME) - { - char buffer[1024]; - TC_TCHARP2CharPBuf(sourcePath, buffer); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), buffer); - return false; - } - - if (cacheSize != -1 && !(xFile->cache = xmalloc(xFile->cacheInitialSize = cacheSize))) // Creates the cache. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - getFullFileName(name, sourcePath, buffer); // Gets the file path. - - // juliana@227_3: improved table files flush dealing. - if (xstrchr(name, '$') || xstrchr(name, '&')) - xFile->dontFlush = true; - - xFile->useCrypto = useCrypto; // juliana@253_8: now Litebase supports weak cryptography. - - // Creates the file or opens it and gets its size. -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -#if defined(POSIX) || defined(ANDROID) - xstrcpy(xFile->fullPath, buffer); - if ((ret = openFile(context, xFile, isCreation? CREATE_EMPTY : READ_WRITE)) -#else - if ((ret = lbfileCreate(&xFile->file, buffer, isCreation? CREATE_EMPTY : READ_WRITE)) -#endif - || (ret = lbfileGetSize(xFile->file, null, (int32*)&xFile->size))) - { - fileError(context, ret, name); - -#if defined(POSIX) || defined(ANDROID) - removeFileFromList(xFile); -#endif - - if (fileIsValid(xFile->file)) - lbfileClose(&xFile->file); - return false; - } - - xstrcpy(xFile->name, name); - return true; -} - -/** - * Reads file bytes. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param buffer The byte array to read data into. - * @param count The number of bytes to read. - * @return false if an error occurs; true, otherwise. - */ -bool nfReadBytes(Context context, XFile* xFile, uint8* buffer, int32 count) -{ - TRACE("nfReadBytes") - - // juliana@202_4: Removed a possible reset or GPF if there is not enough memory to create the file cache on Windows 32, Windows CE, Palm OS, - // and iPhone. - if ((xFile->cacheInitialSize < count || xFile->cachePos < xFile->cacheIni || (xFile->cachePos + count) > xFile->cacheEnd) - && !refreshCache(context, xFile, count)) - return false; - - xmemmove(buffer, &xFile->cache[xFile->cachePos - xFile->cacheIni], count); - - // juliana@253_8: now Litebase supports weak cryptography. - if (xFile->useCrypto) // Decrypts data if asked. - { - int32 i = count; - while (--i >= 0) - *buffer++ ^= 0xAA; - } - - xFile->cachePos += count; // do NOT update xf->pos here! - return true; -} - -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Write bytes in a file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param buffer The byte array to write data from. - * @param count The number of bytes to write. - * @return false if an error occurs; true, otherwise. - */ -bool nfWriteBytes(Context context, XFile* xFile, uint8* buffer, int32 count) -{ - TRACE("nfWriteBytes") - int32 cachePos; - uint8* bufferAux = buffer; - - // juliana@253_8: now Litebase supports weak cryptography. - if (xFile->useCrypto) // Encrypts data if asked. - { - int32 i = count; - while (--i >= 0) - *bufferAux++ ^= 0xAA; - } - - // juliana@202_4: Removed a possible reset or GPF if there is not enough memory to create the file cache on Windows 32, Windows CE, Palm OS, - // and iPhone. - if ((xFile->cacheInitialSize < count || xFile->cachePos < xFile->cacheIni || (xFile->cachePos + count) > xFile->cacheEnd) - && !refreshCache(context, xFile, count)) - return false; - - xmemmove(&xFile->cache[(cachePos = xFile->cachePos) - xFile->cacheIni], buffer, count); - - // juliana@253_8: now Litebase supports weak cryptography. - if (xFile->useCrypto) // Decrypts data if asked. - { - int32 i = count; - while (--i >= 0) - *buffer++ ^= 0xAA; - } - - xFile->cacheIsDirty = true; - xFile->cacheDirtyIni = MIN(cachePos, xFile->cacheDirtyIni); - xFile->cacheDirtyEnd = MAX(cachePos + count, xFile->cacheDirtyEnd); - xFile->position = xFile->cachePos = cachePos + count; - return true; -} - -// juliana@227_3: improved table files flush dealing. -/** - * Enlarges the file. This function MUST be called to grow the file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param newSize The new size for the file. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to grow the file. - */ -bool nfGrowTo(Context context, XFile* xFile, uint32 newSize) -{ - TRACE("nfGrowTo") - int32 ret; - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((ret = reopenFileIfNeeded(context, xFile))) - goto error; -#endif - - // The index files grow a bunch per time, so it is necessary to check here if the growth is really needed. - // If so, enlarges the file. - if ((ret = lbfileSetSize(&xFile->file, newSize))) - goto error; - -// juliana@227_23: solved possible crashes when using a table recovered which was being used with setRowInc(). -#if !defined(POSIX) && !defined(ANDROID) - if (newSize - xFile->size > 0) // juliana@230_18: removed possible garbage in table files. - { - uint8 zeroBuf[1024]; - int32 remains = newSize - xFile->size, - written; - xmemzero(zeroBuf, 1024); - - if ((ret = lbfileSetPos(xFile->file, xFile->size))) - goto error; - while (remains > 0) - { - if ((ret = lbfileWriteBytes(xFile->file, zeroBuf, 0, remains > 1024? 1024 : remains, &written))) - goto error; - remains -= written; - } - - } -#endif - - xFile->position = xFile->size = newSize; - return true; - -error: - fileError(context, ret, xFile->name); - return false; -} - -/** - * Sets the current file position. - * - * @param xFile A pointer to the normal file structure. - * @param newPos The new file position. - */ -void nfSetPos(XFile* xFile, int32 newPos) -{ - TRACE("nfSetPos") - xFile->cachePos = xFile->position = newPos; -} - -// juliana@227_3: improved table files flush dealing. -/** - * Renames a file - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param newName The new name of the file. - * @param sourcePath The path where the file is stored. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to rename the file. - */ -bool nfRename(Context context, XFile* xFile, CharP newName, TCHARP sourcePath) -{ - TRACE("nfRename") - TCHAR oldPath[MAX_PATHNAME]; - TCHAR newPath[MAX_PATHNAME]; - int32 ret; - - getFullFileName(xFile->name, sourcePath, oldPath); - getFullFileName(newName, sourcePath, newPath); - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((ret = reopenFileIfNeeded(context, xFile))) - goto error; -#endif - - // Renames and reopens the file. - if ((ret = lbfileRename(xFile->file, oldPath, newPath, true)) - || (ret = lbfileCreate(&xFile->file, newPath, READ_WRITE))) - { - -#if defined(POSIX) || defined(ANDROID) -error: -#endif - - fileError(context, ret, xFile->name); - return false; - } - - xstrcpy(xFile->name, newName); - -#if defined(POSIX) || defined(ANDROID) - xstrcpy(xFile->fullPath, newPath); -#endif - - return true; -} - -/** - * Closes a file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to close the file. - */ -bool nfClose(Context context, XFile* xFile) -{ - TRACE("nfClose") - int32 ret = 0; - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((ret = reopenFileIfNeeded(context, xFile))) - fileError(context, ret, xFile->name); -#endif - - if (fileIsValid(xFile->file)) - { - // Flushes the cache if necessary and frees it. - if (xFile->cacheIsDirty) - flushCache(context, xFile); - - xfree(xFile->cache); - - // juliana@201_5: the .dbo file must be cropped so that it wont't be too large with zeros at the end of the file. - if (xFile->finalPos && (ret |= lbfileSetSize(&xFile->file, xFile->finalPos))) - fileError(context, ret, xFile->name); - - if ((ret |= lbfileClose(&xFile->file))) - fileError(context, ret, xFile->name); - - fileInvalidate(xFile->file); - } - -#if defined(POSIX) || defined(ANDROID) - removeFileFromList(xFile); -#endif - - return !ret; -} - -/** - * Removes a file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param sourcePath The path where the file is stored. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to remove the file. - */ -bool nfRemove(Context context, XFile* xFile, TCHARP sourcePath) -{ - TRACE("nfRemove") - TCHAR buffer[MAX_PATHNAME]; - int32 ret = 0; - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((ret = reopenFileIfNeeded(context, xFile))) - fileError(context, ret, xFile->name); -#endif - - getFullFileName(xFile->name, sourcePath, buffer); - if ((ret |= lbfileDelete(&xFile->file, buffer, true))) - fileError(context, ret, xFile->name); - fileInvalidate(xFile->file); - xfree(xFile->cache); - -#if defined(POSIX) || defined(ANDROID) - removeFileFromList(xFile); -#endif - - return !ret; -} - -/** - * The cache must be refreshed if what is desired is not inside it. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param count The number of bytes that must be read. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to read from the file. - * @throws OutOfMemoryError If there is not enough memory to enlarge the normal file cache. - */ -bool refreshCache(Context context, XFile* xFile, int32 count) -{ - TRACE("refreshCache") - int32 bytes, - ret; - - if (xFile->cacheIsDirty && !flushCache(context, xFile)) // Flushes the cache if necessary. - return false; - - // juliana@223_14: solved possible memory problems. - if (!xFile->cache || xFile->cacheInitialSize < count) // Increases the cache size if necessary. - if (!(xFile->cache = xrealloc(xFile->cache, xFile->cacheInitialSize = MAX(CACHE_INITIAL_SIZE, count << 2)))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((ret = reopenFileIfNeeded(context, xFile))) - goto error; -#endif - - // Reads data from the file. - if ((ret = lbfileSetPos(xFile->file, xFile->cachePos)) || (ret = lbfileReadBytes(xFile->file, (CharP)xFile->cache, 0, xFile->cacheInitialSize, &bytes))) - { - -#if defined(POSIX) || defined(ANDROID) -error: -#endif - - fileError(context, ret, xFile->name); - return false; - } - - // Updates the cache parameters. - xFile->cacheDirtyEnd = xFile->cacheIni = xFile->cachePos; - xFile->cacheDirtyIni = xFile->cacheEnd = xFile->cacheIni + bytes; - - return true; -} - -/** - * Flushs the cache into the disk. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to write to the file. - */ -bool flushCache(Context context, XFile* xFile) -{ - TRACE("flushCache") - int32 written, - ret; - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -// Some files might have been closed if the maximum number of opened files was reached. -#if defined(POSIX) || defined(ANDROID) - if ((ret = reopenFileIfNeeded(context, xFile))) - goto error; -#endif - - if ((ret = lbfileSetPos(xFile->file, xFile->cacheDirtyIni)) || (ret = lbfileWriteBytes(xFile->file, - (CharP)&xFile->cache[xFile->cacheDirtyIni - xFile->cacheIni], 0, xFile->cacheDirtyEnd - xFile->cacheDirtyIni, &written))) - goto error; - xFile->cacheIsDirty = false; - -// juliana@227_3: improved table files flush dealing. -// juliana@226a_22: solved a problem on Windows CE of file data being lost after a forced reset. -#if defined(POSIX) || defined(ANDROID) - if (!xFile->dontFlush && (ret = lbfileFlush(xFile->file))) - goto error; -#endif - - return true; - -error: - fileError(context, ret, xFile->name); - return false; -} - -/** - * Prepares an error message when an error occurs when dealing with files. - * - * @param context The thread context where the function is being executed. - * @param errorCode The file error code. - * @param fileName The file where the error ocurred. - * @throws DriverException An exception with the error message. - */ -void fileError(Context context, int32 errorCode, CharP fileName) -{ - TRACE("fileError") - char errorMsg[1024]; - - TC_getErrorMessage(errorCode, errorMsg, 1024); - errorMsg[errorCode = xstrlen(errorMsg)] = ' '; - xstrcpy(&errorMsg[errorCode + 1], fileName); - TC_throwExceptionNamed(context, "litebase.DriverException", errorMsg); -} - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -#if defined(POSIX) || defined(ANDROID) -/** - * Opens a disk file to store tables and put it in the files list. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param mode Indicates if the file must be created or just opened. - * @return The error code if an error occurred or zero if the function succeeds. - */ -int32 openFile(Context context, XFile* xFile, int32 mode) -{ - LOCKVAR(files); - if (filesList.count < MAX_OPEN_FILES) // There is space in the list. - { - filesList.list[filesList.count++] = xFile; - xFile->timeStamp = TC_getTimeStamp(); - } - else // No space: the last used file must be removed from the list and closed. - { - int32 ret = MAX_OPEN_FILES, - minStamp, - oldest = 0; - XFile** list = filesList.list; - XFile* file; - - xFile->timeStamp = minStamp = TC_getTimeStamp(); - while (--ret > 0) - if (list[ret]->timeStamp < minStamp) - minStamp = list[oldest = ret]->timeStamp; - - if ((file = list[oldest])->cacheIsDirty && !(ret = flushCache(context, file))) - { - UNLOCKVAR(files); - return 1; - } - - if ((ret = lbfileClose(&file->file))) - { - UNLOCKVAR(files); - return ret; - } - fileInvalidate(list[oldest]->file); - list[oldest] = xFile; - } - UNLOCKVAR(files); - return lbfileCreate(&xFile->file, xFile->fullPath, mode); -} - -/** - * Reopens a file if needed. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @return The error code if an error occurred or zero if the function succeeds. - */ -int32 reopenFileIfNeeded(Context context, XFile* xFile) -{ - if (fileIsValid(xFile->file)) // If the file is opened, just updates its time stamp. - { - xFile->timeStamp = TC_getTimeStamp(); - return 0; - } - else - return openFile(context, xFile, READ_WRITE); // If the file was closed, reopens it. -} - -/** - * Removes a file from the file list, which is open. - * - * @param xFile A pointer to the normal file structure. - */ -void removeFileFromList(XFile* xFile) -{ - int32 i; - XFile** list; - - LOCKVAR(files); - i = filesList.count; - list = filesList.list; - while (--i >= 0 && xFile != list[i]); - if (i >= 0) - { - list[i] = list[--filesList.count]; - list[filesList.count] = null; - } - UNLOCKVAR(files); -} -#endif - diff --git a/LitebaseSDK/src/native/NormalFile.h b/LitebaseSDK/src/native/NormalFile.h deleted file mode 100644 index 1a238f7bcb..0000000000 --- a/LitebaseSDK/src/native/NormalFile.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions for a normal file, ie, a file that is stored on disk. - */ - -#ifndef LITEBASE_NORMAL_FILE_H -#define LITEBASE_NORMAL_FILE_H - -#include "Litebase.h" - -/** - * Creates a disk file to store tables. - * - * @param context The thread context where the function is being executed. - * @param name The name of the file. - * @param isCreation Indicates if the file must be created or just open. - * @param useCrypto Indicates if the table uses cryptography. - * @param sourcePath The path where the file will be created. - * @param xFile A pointer to the normal file structure. - * @param cacheSize The cache size to be used. -1 should be passed if the default value is to be used. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the file cannot be open. - * @throws OutOfMemoryError If there is not enough memory to create the normal file cache. - */ -bool nfCreateFile(Context context, CharP name, bool isCreation, bool useCrypto, TCHARP sourcePath, XFile* xFile, int32 cacheSize); - -/** - * Reads file bytes. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param buffer The byte array to read data into. - * @param count The number of bytes to read. - * @return false if an error occurs; true, otherwise. - */ -bool nfReadBytes(Context context, XFile* xFile, uint8* buffer, int32 count); - -/** - * Write bytes in a file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param buffer The byte array to write data from. - * @param count The number of bytes to write. - * @return false if an error occurs; true, otherwise. - */ -bool nfWriteBytes(Context context, XFile* xFile, uint8* buffer, int32 count); - -/** - * Enlarges the file. This function MUST be called to grow the file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param newSize The new size for the file. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to grow the file. - */ -bool nfGrowTo(Context context, XFile* xFile, uint32 newSize); - -/** - * Sets the current file position. - * - * @param xFile A pointer to the normal file structure. - * @param newPos The new file position. - */ -void nfSetPos(XFile* xFile, int32 newPos); - -/** - * Renames a file - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param newName The new name of the file. - * @param sourcePath The path where the file is stored. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to rename the file. - */ -bool nfRename(Context context, XFile* xFile, CharP newName, TCHARP sourcePath); - -/** - * Closes a file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to close the file. - */ -bool nfClose(Context context, XFile* xFile); - -/** - * Removes a file. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param sourcePath The path where the file is stored. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to remove the file. - */ -bool nfRemove(Context context, XFile* xFile, TCHARP sourcePath); - -/** - * The cache must be refreshed if what is desired is not inside it. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param count The number of bytes that must be read. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to read from the file. - * @throws OutOfMemoryError If there is not enough memory to enlarge the normal file cache. - */ -bool refreshCache(Context context, XFile* xFile, int32 count); - -/** - * Flushs the cache into the disk. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If it is not possible to write to the file. - */ -bool flushCache(Context context, XFile* xFile); - -/** - * Prepares an error message when an error occurs when dealing with files. - * - * @param context The thread context where the function is being executed. - * @param errorCode The file error code. - * @param fileName The file where the error ocurred. - * @throws DriverException An exception with the error message. - */ -void fileError(Context context, int32 errorCode, CharP fileName); - -// juliana@closeFiles_1: removed possible problem of the IOException with the message "Too many open files". -#if defined(POSIX) || defined(ANDROID) -/** - * Opens a disk file to store tables and put it in the files list. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @param mode Indicates if the file must be created or just opened. - * @return The error code if an error occurred or zero if the function succeeds. - */ -int32 openFile(Context context, XFile* xFile, int32 mode); - -/** - * Reopens a file if needed. - * - * @param context The thread context where the function is being executed. - * @param xFile A pointer to the normal file structure. - * @return The error code if an error occurred or zero if the function succeeds. - */ -int32 reopenFileIfNeeded(Context context, XFile* xFile); - -/** - * Removes a file from the file list. - * - * @param xFile A pointer to the normal file structure. - */ -void removeFileFromList(XFile* xFile); -#endif - -#endif diff --git a/LitebaseSDK/src/native/PlainDB.c b/LitebaseSDK/src/native/PlainDB.c deleted file mode 100644 index 188dc6e938..0000000000 --- a/LitebaseSDK/src/native/PlainDB.c +++ /dev/null @@ -1,789 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This has the function definitions for a database in a plain binary file. The data and the metadata (header) is written in one file (.db). The - * strings and the blobs are written in the .dbo file. The current number of records inside the database is discovered only when the database is open - * by getting its size and discounting the header size. This has a double advantage: it is not necessary to waste space storing the current record - * count, and it is not needed to save the record count at each insertion. - * - * This also has function definitions for a temporary database for ResultSet tables. - */ - -#include "PlainDB.h" - -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Creates a new PlainDB, loading or creating the table with the given name or creating a temporary table. - * - * @param context The thread context where the function is being executed. - * @param plainDB Receives the new PlainDB or null if an error occurs. - * @param name The name of the table. - * @param create Defines if the file will be created if it doesn't exist. - * @param useCrypto Indicates if the table uses cryptography. - * @param sourcePath The path where the table is to be open or created. - * @return false if an error occurs; true, otherwise. - */ -bool createPlainDB(Context context, PlainDB* plainDB, CharP name, bool create, bool useCrypto, TCHARP sourcePath) -{ - TRACE("createPlainDB") - char buffer[DBNAME_SIZE]; - - plainDB->rowInc = name? DEFAULT_ROW_INC : 100; // Sets row incrementor. - plainDB->headerSize = (uint16)DEFAULT_HEADER; // Sets the initial header size. - - if (name) - { - // Sets the .db name if the file is a normal file. - xstrcpy(plainDB->name, name); - xstrcpy(buffer, name); - xstrcat(buffer, DB_EXT); - - // Sets the normal file function pointers. - plainDB->setPos = nfSetPos; - plainDB->growTo = nfGrowTo; - plainDB->readBytes = nfReadBytes; - plainDB->writeBytes = nfWriteBytes; - plainDB->close = nfClose; - // Opens or creates the .db and .dbo files. - if (nfCreateFile(context, buffer, create, useCrypto, sourcePath, &plainDB->db, -1) - && xstrcat(buffer, "o") - && nfCreateFile(context, buffer, create, useCrypto, sourcePath, &plainDB->dbo, -1)) - return true; - } - else - { - // Sets the memory file function pointers. - plainDB->setPos = mfSetPos; - plainDB->growTo = mfGrowTo; - plainDB->readBytes = mfReadBytes; - plainDB->writeBytes = mfWriteBytes; - plainDB->close = mfClose; - return true; - } - - plainClose(context, plainDB, false); // Closes the table files if an error occurs. - return false; -} - -/** - * Sets the size of a row. - * - * @param plainDB The PlainDB. - * @param newRowSize The new row size. - * @param buffer A buffer for the PlainDB. - */ -void plainSetRowSize(PlainDB* plainDB, int32 newRowSize, uint8* buffer) -{ - TRACE("plainSetRowSize") - plainDB->rowSize = newRowSize; - plainDB->basbuf = buffer; - - if (plainDB->db.size >= plainDB->headerSize) // Finds how many records are there. - plainDB->rowCount = (plainDB->db.size - plainDB->headerSize)/ plainDB->rowSize; -} - -/** - * Adds a new record. The file pointer is positioned in the record's beginning so that the data can be written. Usually the record is first added, - * then the contents are written. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @return false if an error occurs; true, otherwise. - */ -bool plainAdd(Context context, PlainDB* plainDB) -{ - TRACE("plainAdd") - - if (--plainDB->rowAvail <= 0) // Checks if there are no more space pre-allocated. - { - if (!plainDB->growTo(context, &plainDB->db, (plainDB->rowCount + plainDB->rowInc) * plainDB->rowSize + plainDB->headerSize)) - return false; - plainDB->rowAvail = plainDB->rowInc; - } - return plainSetPos(context, plainDB, plainDB->rowCount); // Sets the position to the start of the record. -} - -/** - * Writes the data of the .db file buffer into the current file position. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @return false if an error occurs; true, otherwise. - */ -bool plainWrite(Context context, PlainDB* plainDB) -{ - TRACE("plainWrite") - if (plainDB->writeBytes(context, &plainDB->db, plainDB->basbuf, plainDB->rowSize)) - { - plainDB->rowCount++; - return true; - } - return false; -} - -/** - * Reads a row at the given position into the .db file buffer. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param record The record to be read. - * @return false if an error occurs; true, otherwise. - */ -bool plainRead(Context context, PlainDB* plainDB, int32 record) -{ - TRACE("plainRead") - return plainSetPos(context, plainDB, record) && plainDB->readBytes(context, &plainDB->db, plainDB->basbuf, plainDB->rowSize); -} - -/** - * Rewrites a row at the given position. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param record The .db file record to be read. - * @return false if an error occurs; true, otherwise. - */ -bool plainRewrite(Context context, PlainDB* plainDB, int32 record) -{ - TRACE("plainRewrite") - return plainSetPos(context, plainDB, record) && plainDB->writeBytes(context, &plainDB->db, plainDB->basbuf, plainDB->rowSize); -} - -/** - * Renames the files to the new given name. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param newName The new table name. - * @param sourcePath The files path. - * @return false if an error occurs; true, otherwise. - */ -bool plainRename(Context context, PlainDB* plainDB, CharP newName, TCHARP sourcePath) -{ - TRACE("plainRename") - char buffer[DBNAME_SIZE]; - int32 finalPos = plainDB->dbo.finalPos; - - xstrcpy(buffer, newName); - xstrcat(buffer, DB_EXT); - - // juliana@202_1: .db should be renamed back if .dbo can't be renamed. - if (nfRename(context, &plainDB->db, buffer, sourcePath)) // Renames the .db file. - { - xstrcat(buffer, "o"); - if (!nfRename(context, &plainDB->dbo, buffer, sourcePath)) // Renames the .dbo file. - { - // If the file could not be renamed, which is unlikely to occur, the .db file should be renamed back. - xstrcpy(buffer, plainDB->name); - xstrcat(buffer, DB_EXT); - nfRename(context, &plainDB->db, buffer, sourcePath); - return false; - } - } - else - return false; - - plainDB->dbo.finalPos = finalPos; - return true; -} - -/** - * Writes the given metadata to the header of the .db file. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param buffer The data to be written. - * @param length The data length. - * @return false if an error occurs; true, otherwise. - */ -bool plainWriteMetaData(Context context, PlainDB* plainDB, uint8* buffer, int32 length) -{ - TRACE("plainWriteMetaData") - int32 headerSize = plainDB->headerSize; - XFile* db = &plainDB->db; - - if (!plainDB->db.size) // The metadata size must have a free space for future composed indices or composed primary key. - { - // juliana@230_7: corrected a possible exception or crash when the table has too many columns and composed indices or PKs. - while (length > headerSize || headerSize - length < COMP_IDX_PK_SIZE) - headerSize <<= 1; - if (!nfGrowTo(context, db, plainDB->headerSize = headerSize)) - return false; - - // juliana@223_15: solved a bug that could corrupt tables created with a very large metadata size. - buffer[4] = (uint8)headerSize; - buffer[5] = (uint8)(headerSize >> 8); - } - nfSetPos(db, 0); - - if (db->useCrypto) // juliana@253_8: now Litebase supports weak cryptography. - { - int32 i = 4; - while (--i >= 0) - *buffer++ ^= 0xAA; - buffer -= 4; - } - - return nfWriteBytes(context, db, buffer, length); -} - -/** - * Reads the user metadata from the .db file header. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param buffer An static buffer for reading the metadata. - * @return The metadata. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - * @return null if an error occurs; a metadata buffer, otherwise. - */ -uint8* plainReadMetaData(Context context, PlainDB* plainDB, uint8* buffer) -{ - TRACE("plainReadMetaData") - - if (!buffer) // Allocates the buffer for the metadata. - if (!(buffer = (uint8*)xmalloc(plainDB->headerSize))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - - // Fetches and reads the metadata. - nfSetPos(&plainDB->db, 0); - - // juliana@223_14: solved possible memory problems. - if (!nfReadBytes(context, &plainDB->db, buffer, plainDB->headerSize)) - { - if (plainDB->headerSize != DEFAULT_HEADER) - xfree(buffer); - return null; - } - - if (plainDB->db.useCrypto) // juliana@253_8: now Litebase supports weak cryptography. - { - int32 i = 4; - while (--i >= 0) - *buffer++ ^= 0xAA; - buffer -= 4; - } - - return buffer; -} - -/** - * Closes the table files. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param updatePos Indicates if finalPos must be re-calculated to shrink the file. - * @return false if an error occurs; true, otherwise. - */ -bool plainClose(Context context, PlainDB* plainDB, bool updatePos) -{ - TRACE("plainClose") - bool ret = true; - - if (plainDB) - { - if (plainDB->db.fbuf || fileIsValid(plainDB->db.file) || plainDB->db.cache) - { - if (*plainDB->name) - { - uint8 buffer[7]; - uint8* pointer = buffer; - - // Stores the changeable information. - // juliana@253_8: now Litebase supports weak cryptography. - xmemzero(buffer, 4); - *pointer = (plainDB->db.useCrypto? (plainDB->useOldCrypto? 1 : USE_CRYPTO) : 0); - xmove2(pointer + 4, &plainDB->headerSize); - pointer += 6; - - // The table format must also be saved. - *pointer++ = plainDB->isAscii? IS_ASCII | !plainDB->wasNotSavedCorrectly : !plainDB->wasNotSavedCorrectly; - - ret = plainWriteMetaData(context, plainDB, buffer, 7); - - if (updatePos) // Calculates .db used space: .db won't have zeros at the end. - plainDB->db.finalPos = plainDB->rowCount * plainDB->rowSize + plainDB->headerSize; - } - ret &= plainDB->close(context, &plainDB->db); // Closes .db. - } - if (plainDB->dbo.fbuf || fileIsValid(plainDB->dbo.file) || plainDB->dbo.cache) // Closes .dbo if it's open. - ret &= plainDB->close(context, &plainDB->dbo); - } - return ret; -} - -/** - * Removes the table files. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param sourcePath The files path. - * @return false if an error occurs; true, otherwise. - */ -bool plainRemove(Context context, PlainDB* plainDB, TCHARP sourcePath) -{ - TRACE("plainRemove") - bool ret = true; - - if (fileIsValid(plainDB->db.file) || plainDB->db.cache) - ret = nfRemove(context, &plainDB->db, sourcePath); - if (fileIsValid(plainDB->dbo.file) || plainDB->dbo.cache) - ret &= nfRemove(context, &plainDB->dbo, sourcePath); - - return ret; -} - -/** - * Sets the .db file pointer to point to a record position. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param record The record position. - * @return false if an error occurs; true, otherwise. - */ -bool plainSetPos(Context context, PlainDB* plainDB, int32 record) -{ - TRACE("plainSetPos") - if (record >= 0 && record < (plainDB->rowCount + plainDB->rowAvail)) - { - // If the table grows too much, the real position can be negative because the integer size has a limit. - int32 value = record * plainDB->rowSize + (*plainDB->name? plainDB->headerSize : 0); - if (value < 0) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_POS), record); - return false; - } - plainDB->setPos(&plainDB->db, value); - return true; - } - if (record >= 0) - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_POS), record); - return false; -} - -// guich@201_9: always shrink the .db and .dbo memory files. -/** - * Compresses the memory file buffers at the current position. This is necessary so that the memory tables do not have unused space after computing - * the select. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @return false if an error occurs; true, otherwise. - */ -bool plainShrinkToSize(Context context, PlainDB* plainDB) -{ - TRACE("plainShrinkToSize") - if (plainDB->rowCount > 0 && plainDB->rowAvail > 0) - { - uint32 ret = plainDB->rowCount * plainDB->rowSize; - if (plainDB->db.size != ret) // Shrinks the .db. - { - if (mfGrowTo(context, &plainDB->db, ret)) - { - plainDB->db.size = ret; - plainDB->rowAvail = 0; - } - else - return false; - } - if ((int32)plainDB->dbo.size != plainDB->dbo.finalPos) - { - if (mfGrowTo(context, &plainDB->dbo, plainDB->dbo.finalPos)) // Shrinks the .dbo. - plainDB->dbo.size = plainDB->dbo.finalPos; - else - return false; - } - } - return true; -} - -/** - * Reads a value from a PlainDB. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param value The value to be read. - * @param offset The offset of the value in its row. - * @param colType The type of the value. - * @param buffer The buffer where the row data is stored. - * @param isTemporary Indicates if this is a result set table or the value or the integer of a rowid index is to be loaded. - * @param isNull Indicates if the value is null. - * @param isTempBlob Indicates if the blob is being read for a temporary table. - * @param size The column size of the string being read. - * @param heap A heap to allocate temporary strings. - * @return false if an error occurs; true, otherwise. - */ -bool readValue(Context context, PlainDB* plainDB, SQLValue* value, int32 offset, int32 colType, uint8* buffer, bool isTemporary, bool isNull, - bool isTempBlob, int32 size, Heap heap) -{ - TRACE("readValue") - buffer += offset; - if (!isNull) - switch (colType) - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case CHARS_NOCASE_TYPE: - case CHARS_TYPE: - { - int32 length = 0, - position; - XFile* dbo; - - xmove4(&position, buffer); - - if (isTempBlob && !*plainDB->name) - { - uint8* ptrStr = plainDB->dbo.fbuf + position; - xmove4(&position, ptrStr); - ptrStr += 4; - xmoveptr(&plainDB, ptrStr); - } - - if (position < 0) // juliana@270_22: solved a possible crash when the table is corrupted on Android and possibly on other platforms. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_POS), position); - return false; - } - - // juliana@253_8: now Litebase supports weak cryptography. - else if (position > (dbo = &plainDB->dbo)->finalPos) - { - value->length = 0; - return true; - } - - // Reads the string position in the .dbo and sets its position. - plainDB->setPos(dbo, value->asInt = position); - - value->asBlob = (uint8*)plainDB; // Holds the plainDB pointer so the string don't need to be loaded in the temporary table. - - // Reads the string size. If it is zero nothing is read. - if (!plainDB->readBytes(context, dbo, (uint8*)&length, 2)) - return false; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - if (size != -1 && length > size) - length = size; - - // juliana@253_5: removed .idr files from all indices and changed its format. - if (!(value->length = length) && !value->asChars) - { - value->asChars = (JCharP)""; - return true; - } - - if (!value->asChars) // Allocates the string if it was not previoulsy allocated. - value->asChars = (JCharP)TC_heapAlloc(heap, length << 1); - - return loadString(context, plainDB, value->asChars, length); - } - case SHORT_TYPE: - xmove2(&value->asShort, buffer); // Reads the short. - break; - - case INT_TYPE: - xmove4(&value->asInt, buffer); // Reads the int. - if (!offset && !isTemporary) // Is it the row id? - value->asInt &= ROW_ID_MASK; // Masks out the attributes. - break; - - case LONG_TYPE: - xmove8(&value->asLong, buffer); // Reads the long. - break; - - case FLOAT_TYPE: - xmove4(&value->asFloat, buffer); // Reads the float. - break; - - case DOUBLE_TYPE: - READ_DOUBLE((uint8*)&value->asDouble, buffer); // Reads the double. - break; - - case DATE_TYPE: - xmove4(&value->asInt, buffer); // Reads the date. - break; - - case DATETIME_TYPE: // rnovais@567_2 - xmove4(&value->asDate, buffer); // Reads the date. - xmove4(&value->asTime, buffer + 4); // Reads the time. - break; - - case BLOB_TYPE: // juliana@220_3: blobs are not loaded anymore in the temporary table and sortings. - if (isTempBlob) // A blob is being read to a temporary table. - { - xmove4(&value->asInt, buffer); - value->asBlob = (uint8*)plainDB; - } - else - { - int32 position, - length; - XFile* dbo = &plainDB->dbo; - - // Reads and sets the blob position in the .dbo. - xmove4(&position, buffer); - - if (position > dbo->finalPos) - { - value->length = 0; - return true; - } - else if (position < 0) // juliana@270_22: solved a possible crash when the table is corrupted on Android and possibly on other platforms. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_POS), position); - return false; - } - - plainDB->setPos(dbo, position); - - if (!plainDB->readBytes(context, dbo, (uint8*)&length, 4)) // Reads the blob size; - return false; - - if (length < 0) // juliana@253_8: now Litebase supports weak cryptography. - length = 0; - if (size != -1 && length > size) - length = size; - - // If the size is zero nothing is read. - if ((value->length = length) > 0 && value->asBlob && !plainDB->readBytes(context, dbo, value->asBlob, length)) - return false; - } - } - return true; -} - -/** - * Writes a value to a table column. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param value The value to be written. - * @param buffer The buffer where the value is stored before going to the table. - * @param colType The type of the column. - * @param colSize The column size of the value. - * @param isValueOk Indicates if the value is to be written. - * @param addingNewRecord Indicates if it is an update or an insert. - * @param isNull Indicates that the value being inserted is a null. - * @paran isTempBlob Indicates if a temporary table is being used. - * @return false if an error occurs; true, otherwise. - */ -bool writeValue(Context context, PlainDB* plainDB, SQLValue* value, uint8* buffer, int32 colType, int32 colSize, bool isValueOk, bool addingNewRecord, bool isNull, bool isTempBlob) -{ - TRACE("writeValue") - - if (isValueOk && !isNull) - switch (colType) - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case CHARS_NOCASE_TYPE: - case CHARS_TYPE: - if (isTempBlob) - { - int32 size; - XFile* dbo = &plainDB->dbo; - - if ((dbo->finalPos + (size = TSIZE + 4)) > (int32)dbo->size - && !plainDB->growTo(context, dbo, dbo->size + size * MAX(16, plainDB->rowInc))) - return false; - - plainDB->setPos(dbo, dbo->finalPos); - xmove4(buffer, &dbo->position); - - // Saves the .dbo position and the physical plainDB pointer. - if (!plainDB->writeBytes(context, dbo, (uint8*)&value->asInt, 4) - || !plainDB->writeBytes(context, dbo, (uint8*)&value->asBlob, TSIZE)) - return false; - dbo->finalPos = dbo->position; - return true; - } - else - { - int32 length = value->length, // juliana@225_7: the string that is bigger than its field definiton was already trimmed. - size = plainDB->isAscii? (2 + length) : (2 + (length << 1)), // Computes the string size. - ret = true; - XFile* dbo = &plainDB->dbo; - - // juliana@201_20: only grows .dbo if it is going to be increased. - // juliana@202_21: Always writes the string at the end of the .dbo. This removes possible bugs when doing updates. - // guich@201_8: grows using rowInc instead of 16 if rowInc > 16. - if ((dbo->finalPos + size) > (int32)dbo->size && !plainDB->growTo(context, dbo, dbo->size + size * MAX(16, plainDB->rowInc))) - return false; - - plainDB->setPos(dbo, dbo->finalPos); - xmove4(buffer, &dbo->position); - value->asInt = dbo->position; // The string position for an index. - - // Writes the string. - if (plainDB->isAscii) // juliana@210_2: now Litebase supports tables with ascii strings. - { - int32 i = length; - CharP from = (CharP)value->asChars, - to = (CharP)value->asChars; - - while (--i >= 0) // Transforms the unicode string into an ascii string. - { - *to++ = *from; - from += 2; - } - - if (!plainDB->writeBytes(context, dbo, (uint8*)&length, 2) - || (length && !plainDB->writeBytes(context, dbo, (uint8*)value->asChars, length))) - ret = false; - - // The string MUST be transformed back. Otherwise, it may mess a Java string up. - from = (CharP)value->asChars + (i = length - 1); - to = from + i; - while (--i >= 0) - { - *to = *from; - *from-- = 0; - to -= 2; - } - } - else if (!plainDB->writeBytes(context, dbo, (uint8*)&length, 2) - || (length && !plainDB->writeBytes(context, dbo, (uint8*)value->asChars, length << 1))) - return false; - - dbo->finalPos = dbo->position; // juliana@202_21: the final positon now is always the new positon. - return ret; - } - - case SHORT_TYPE: - xmove2(buffer, &value->asShort); - break; - - case DATE_TYPE: // rnovais@567_2 - case INT_TYPE: - xmove4(buffer, &value->asInt); - break; - - case LONG_TYPE: - xmove8(buffer, &value->asLong); - break; - - case FLOAT_TYPE: - xmove4(buffer, &value->asFloat); - break; - - case DOUBLE_TYPE: - READ_DOUBLE(buffer, (uint8*)&value->asDouble); - break; - - case DATETIME_TYPE: // rnovais@567_2 - xmove4(buffer, &value->asDate); // Writes the date. - xmove4(buffer + 4, &value->asTime); // Writes the time. - break; - - case BLOB_TYPE: - { - int32 size; - XFile* dbo = &plainDB->dbo; - - if (isTempBlob) // juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. - { - // guich@201_8: grows using rowInc instead of 16 if rowInc > 16. - // If the .dbo is full, grows it. - if ((dbo->finalPos + (size = TSIZE + 4)) > (int32)dbo->size - && !plainDB->growTo(context, dbo, dbo->size + size * MAX(16, plainDB->rowInc))) - return false; - - plainDB->setPos(dbo, dbo->finalPos); - xmove4(buffer, &dbo->position); - - // Saves the .dbo position and the physical plainDB pointer. - if (!plainDB->writeBytes(context, dbo, (uint8*)&value->asInt, 4) - || !plainDB->writeBytes(context, dbo, (uint8*)&value->asBlob, TSIZE)) - return false; - dbo->finalPos = dbo->position; - } - else - { - int32 oldPos = 0, - length = MIN((int32)value->length, colSize); - - size = length + 4; - - // juliana@201_20: only grows .dbo if it is going to be increased. - if (addingNewRecord && (dbo->finalPos + size) > (int32)dbo->size - && !plainDB->growTo(context, dbo, dbo->size + size * MAX(16, plainDB->rowInc))) // guich@201_8: grow using rowInc instead of 16 if rowInc > 16 - return false; - - // It is an insert or the size of the blob is greater then the old, writes the blob at the end of the .dbo. - if (addingNewRecord) - plainDB->setPos(dbo, dbo->finalPos); - else - plainDB->setPos(&plainDB->dbo, oldPos = dbo->position); - - // Writes its position in the buffer. - xmove4(buffer, &dbo->position); - - // Writes the blob size to .dbo and the blob itself to .dbo. - if (!plainDB->writeBytes(context, dbo, (uint8*)&length, 4) - || (length && !plainDB->writeBytes(context, dbo, value->asBlob, length))) - return false; - - if (addingNewRecord) // It is an insert or the size of the blob is greater then the old one, the final positon is the new positon. - dbo->finalPos = dbo->position; - else // Otherwise, restores the old position. - plainDB->setPos(dbo, oldPos + size + 4); - } - } - } - return true; -} - -/** - * Tests if a record of a table is not deleted. - * - * @param buffer The buffer where the table record is stored. - * @return false if the record is deleted; true otherwise. - */ -int32 recordNotDeleted(uint8* buffer) -{ - TRACE("recordNotDeleted") - int32 attr; - - xmove4(&attr, buffer); - return (attr & ROW_ATTR_MASK) != ROW_ATTR_DELETED; // When a row is deleted, its rowid is set to 0. -} - -/** - * Loads a string from a table taking the storage format into consideration. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param string The buffer where the string will be stored. - * @param length The length of the string to be loaded. - * @return false if an error occurs; true, otherwise. - */ -bool loadString(Context context, PlainDB* plainDB, JCharP string, int32 length) -{ - TRACE("loadString") - - if (plainDB->isAscii) // juliana@210_2: now Litebase supports tables with ascii strings. - { - int32 i = length - 1; - CharP str = (CharP)string, - from = str + i, - to = from + i; - - if (!plainDB->readBytes(context, &plainDB->dbo, (uint8*)str, length)) // Reads the string. - return false; - - while (--i >= 0) - { - *to = *from; - *from-- = 0; - to -= 2; - } - } - else if (!plainDB->readBytes(context, &plainDB->dbo, (uint8*)string, length << 1)) // Reads the string. - return false; - return true; -} diff --git a/LitebaseSDK/src/native/PlainDB.h b/LitebaseSDK/src/native/PlainDB.h deleted file mode 100644 index 1a5dd357d4..0000000000 --- a/LitebaseSDK/src/native/PlainDB.h +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This has the function declarations for a database in a plain binary file. The data and the metadata (header) is written in one file (.db). The - * strings and the blobs are written in the .dbo file. The current number of records inside the database is discovered only when the database is open - * by getting its size and discounting the header size. This has a double advantage: it is not necessary to waste space storing the current record - * count, and it is not needed to save the record count at each insertion. - * - * This also has function declarations for a temporary database for ResultSet tables. - */ - -#ifndef LITEBASE_PLAINDB_H -#define LITEBASE_PLAINDB_H - -#include "Litebase.h" - -/** - * Creates a new PlainDB, loading or creating the table with the given name or creating a temporary table. - * - * @param context The thread context where the function is being executed. - * @param plainDB Receives the new PlainDB or null if an error occurs. - * @param name The name of the table. - * @param create Defines if the file will be created if it doesn't exist. - * @param useCrypto Indicates if the table uses cryptography. - * @param sourcePath The path where the table is to be open or created. - * @return false if an error occurs; true, otherwise. - */ -bool createPlainDB(Context context, PlainDB* plainDB, CharP name, bool create, bool useCrypto, TCHARP sourcePath); - -/** - * Sets the size of a row. - * - * @param plainDB The PlainDB. - * @param newRowSize The new row size. - * @param buffer A buffer for the PlainDB. - */ -void plainSetRowSize(PlainDB* plainDB, int32 newRowSize, uint8* buffer); - -/** - * Adds a new record. The file pointer is positioned in the record's beginning so that the data can be written. Usually the record is first added, - * then the contents are written. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @return false if an error occurs; true, otherwise. - */ -bool plainAdd(Context context, PlainDB* plainDB); - -/** - * Writes the data of the .db file buffer into the current file position. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @return false if an error occurs; true, otherwise. - */ -bool plainWrite(Context context, PlainDB* plainDB); - -/** - * Reads a row at the given position into the .db file buffer. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param record The record to be read. - * @return false if an error occurs; true, otherwise. - */ -bool plainRead(Context context, PlainDB* plainDB, int32 record); - -/** - * Rewrites a row at the given position. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param record The .db file record to be read. - * @return false if an error occurs; true, otherwise. - */ -bool plainRewrite(Context context, PlainDB* plainDB, int32 record); - -/** - * Renames the files to the new given name. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param newName The new table name. - * @param sourcePath The files path. - * @return false if an error occurs; true, otherwise. - */ -bool plainRename(Context context, PlainDB* plainDB, CharP newName, TCHARP sourcePath); - -/** - * Writes the given metadata to the header of the .db file. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param buffer The data to be written. - * @param length The data length. - * @return false if an error occurs; true, otherwise. - */ -bool plainWriteMetaData(Context context, PlainDB* plainDB, uint8* buffer, int32 length); - -/** - * Reads the user metadata from the .db file header. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param buffer An static buffer for reading the metadata. - * @return The metadata. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - * @return null if an error occurs; a metadata buffer, otherwise. - */ -uint8* plainReadMetaData(Context context, PlainDB* plainDB, uint8* buffer); - -/** - * Closes the table files. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param updatePos Indicates if finalPos must be re-calculated to shrink the file. - * @return false if an error occurs; true, otherwise. - */ -bool plainClose(Context context, PlainDB* plainDB, bool updatePos); - -/** - * Removes the table files. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param sourcePath The files path. - * @return false if an error occurs; true, otherwise. - */ -bool plainRemove(Context context, PlainDB* plainDB, TCHARP sourcePath); - -/** - * Sets the .db file pointer to point to a record position. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param record The record position. - * @return false if an error occurs; true, otherwise. - */ -bool plainSetPos(Context context, PlainDB* plainDB, int32 record); - -/** - * Compresses the memory file buffers at the current position. This is necessary so that the memory tables do not have unused space after computing - * the select. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @return false if an error occurs; true, otherwise. - */ -bool plainShrinkToSize(Context context, PlainDB* plainDB); - -/** - * Reads a value from a PlainDB. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param value The value to be read. - * @param offset The offset of the value in its row. - * @param colType The type of the value. - * @param buffer The buffer where the row data is stored. - * @param isTemporary Indicates if this is a result set table or the value or the integer of a rowid index is to be loaded. - * @param isNull Indicates if the value is null. - * @param isTempBlob Indicates if the blob is being read for a temporary table. - * @param size The column size of the string being read. - * @param heap A heap to allocate temporary strings. - * @return false if an error occurs; true, otherwise. - */ -bool readValue(Context context, PlainDB* plainDB, SQLValue* value, int32 offset, int32 colType, uint8* buffer, bool isTemporary, bool isNull, - bool isTempBlob, int32 size, Heap heap); - -/** - * Writes a value to a table column. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param value The value to be written. - * @param buffer The buffer where the value is stored before going to the table. - * @param colType The type of the column. - * @param colSize The column size of the value. - * @param isValueOk Indicates if the value is to be written. - * @param addingNewRecord Indicates if it is an update or an insert. - * @param isNull Indicates that the value being inserted is a null. - * @paran isTempBlob Indicates if a temporary table is being used. - * @return false if an error occurs; true, otherwise. - */ -bool writeValue(Context context, PlainDB* plainDB, SQLValue* value, uint8* buffer, int32 colType, int32 colSize, bool isValueOk, bool addingNewRecord, bool isNull, bool isTempBlob); - -/** - * Tests if a record of a table is not deleted. - * - * @param buffer The buffer where the table record is stored. - * @return false if the record is deleted; true otherwise. - */ -bool recordNotDeleted(uint8* buffer); - -/** - * Loads a string from a table taking the storage format into consideration. - * - * @param context The thread context where the function is being executed. - * @param plainDB The PlainDB. - * @param string The buffer where the string will be stored. - * @param length The length of the string to be loaded. - * @return false if an error occurs; true, otherwise. - */ -bool loadString(Context context, PlainDB* plainDB, JCharP string, int32 length); - -#endif diff --git a/LitebaseSDK/src/native/PreparedStatement.c b/LitebaseSDK/src/native/PreparedStatement.c deleted file mode 100644 index c08b1f0cf5..0000000000 --- a/LitebaseSDK/src/native/PreparedStatement.c +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines functions to deal with important prepared statements. - */ - -#include "Litebase.h" - -/** - * Frees a prepared statement. - * - * @param unused Parameter for htFree(). - * @param statement The prepared statement to be freed. - */ -void freePreparedStatement(int32 unused, TCObject statement) -{ - TRACE("freePreparedStatement") - UNUSED(unused) - - // juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - if (!OBJ_PreparedStatementDontFinalize(statement)) // The prepared statement shouldn't be finalized twice. - { - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(statement); - int32 numParams = OBJ_PreparedStatementStoredParams(statement); - TCObjects* psList; - Table* table; - Heap heap = null; - - switch (OBJ_PreparedStatementType(statement)) // Destroy the statement. - { - case CMD_DELETE: - { - SQLDeleteStatement* deleteStmt = (SQLDeleteStatement*)getPreparedStatementStatement(statement); - - // Removes the prepared statement from table list. - psList = (table = deleteStmt->rsTable->table)->preparedStmts; - psList = TC_TCObjectsRemove(psList, statement); - table->preparedStmts = psList; - - heap = deleteStmt->heap; - break; - } - case CMD_INSERT: - { - SQLInsertStatement* insertStmt = (SQLInsertStatement*)getPreparedStatementStatement(statement); - - // Removes the prepared statement from table list. - psList = insertStmt->table->preparedStmts; - psList = TC_TCObjectsRemove(psList, statement); - insertStmt->table->preparedStmts = psList; - - heap = insertStmt->heap; - break; - } - case CMD_SELECT: - { - SQLSelectStatement* selectStmt = (SQLSelectStatement*)getPreparedStatementStatement(statement); - SQLSelectClause* selectClause = selectStmt->selectClause; - SQLResultSetTable** tableList = selectClause->tableList; - int32 i = selectClause->tableListSize; - - while (--i >= 0) // Removes the prepared statement from table list. - { - psList = (table = tableList[i]->table)->preparedStmts; - psList = TC_TCObjectsRemove(psList, statement); - table->preparedStmts = psList; - } - - heap = selectClause->heap; - break; - } - case CMD_UPDATE: - { - SQLUpdateStatement* updateStmt = (SQLUpdateStatement*)getPreparedStatementStatement(statement); - - // Removes the prepared statement from table list. - psList = (table = updateStmt->rsTable->table)->preparedStmts; - psList = TC_TCObjectsRemove(psList, statement); - table->preparedStmts = psList; - - heap = updateStmt->heap; - } - } - - // Frees logger information. - while (--numParams >= 0) - xfree(paramsAsStrs[numParams]); - - heapDestroy(heap); - OBJ_PreparedStatementDontFinalize(statement) = true; - TC_setObjectLock(statement, UNLOCKED); // juliana@226a_21 - } -} - -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Sets numeric parameters in a prepared statement. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i32[1] The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool psSetNumericParamValue(NMParams p, int32 type) -{ - TRACE("psSetNumericParamValue") - - if (testPSClosed(p)) - { - TCObject stmt = p->obj[0]; - Context context = p->currentContext; - SQLSelectStatement* selectStmt = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - - if (selectStmt) // Only sets the parameter if the statement is not null. - { - int32 index = p->i32[0]; - VoidP value = null; - - switch (type) // Gets a pointer for the value. - { - case SHORT_TYPE: - case INT_TYPE: - value = &p->i32[1]; - break; - case LONG_TYPE: - value = &p->i64[0]; - break; - case FLOAT_TYPE: - value = &p->dbl[0]; - break; - case DOUBLE_TYPE: - value = &p->dbl[0]; - } - - switch (selectStmt->type) // Sets the parameter. - { - case CMD_DELETE: - if (!setNumericParamValueDel(context, (SQLDeleteStatement*)selectStmt, index, value, type)) - return false; - break; - case CMD_INSERT: - if (!setNumericParamValueIns(context, (SQLInsertStatement*)selectStmt, index, value, type)) - return false; - break; - case CMD_SELECT: - if (!setNumericParamValueSel(context, selectStmt, index, value, type)) - return false; - break; - case CMD_UPDATE: - if (!setNumericParamValueUpd(context, (SQLUpdateStatement*)selectStmt, index, value, type)) - return false; - } - - if (OBJ_PreparedStatementStoredParams(stmt)) // Only stores the parameter if there are parameters to be stored. - { - CharP ptr = null; - int16* paramsLength = getPreparedStatementParamsLength(stmt); - int32 length, - maxLength = paramsLength[index]; - - // juliana@214_2: corrected a bug that could crash the application when using logger. - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(stmt); - JCharP string = paramsAsStrs[index]; - DoubleBuf buffer; - - switch (type) // Transforms the number into a string. - { - case SHORT_TYPE: - case INT_TYPE: - { - ptr = TC_int2str(*(int32*)value, buffer); - break; - } - case LONG_TYPE: - { - ptr = TC_long2str(*(int64*)value, buffer); - break; - } - case FLOAT_TYPE: - case DOUBLE_TYPE: - ptr = TC_double2str(*(double*)value, -1, buffer); - } - - // Stores the parameter. - if ((length = xstrlen(ptr)) > maxLength) - { - paramsLength[index] = length; - xfree(paramsAsStrs[index]); - if (!(string = paramsAsStrs[index] = TC_CharP2JCharP(ptr, length))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - } - else - TC_CharP2JCharPBuf(ptr, length, string, true); - } - } - return true; - } - return false; -} - -// juliana@238_1: corrected the end quote not appearing in the log files after dates. -/** - * Sets a string parameter in a prepared statement. - * - * @param context The thread context where the function is being executed. - * @param stmt The prepared statement object. - * @param string The string object to be inserted. - * @param index The parameter index. - * @param stringLength The string length. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool psSetStringParamValue(Context context, TCObject stmt, TCObject string, int32 index, int32 stringLength) -{ - SQLSelectStatement* statement = (SQLSelectStatement*)getPreparedStatementStatement(stmt); - JCharP stringChars = null; - - if (string) - stringChars = String_charsStart(string); - - switch (statement->type) // Sets the parameter. - { - case CMD_DELETE: - if (!setParamValueStringDel(context, (SQLDeleteStatement*)statement, index, stringChars, stringLength)) - return false; - break; - case CMD_INSERT: - if (!setStrBlobParamValueIns(context, (SQLInsertStatement*)statement, index, stringChars, stringLength, true)) - return false; - break; - case CMD_SELECT: - if (!setParamValueStringSel(context, statement, index, stringChars, stringLength)) - return false; - break; - case CMD_UPDATE: - if (!setStrBlobParamValueUpd(context, (SQLUpdateStatement*)statement, index, stringChars, stringLength, true)) - return false; - } - - if (OBJ_PreparedStatementStoredParams(stmt)) // Only stores the parameter if there are parameters to be stored. - { - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(stmt); - JCharP paramAsStr = paramsAsStrs[index]; - int16* paramsLength = getPreparedStatementParamsLength(stmt); - - if (string) // The parameter is not null. - { - if (stringLength + 2 > paramsLength[index]) // Reuses the buffer whenever possible - { - if (!(paramAsStr = paramsAsStrs[index] = (JCharP)xrealloc((uint8*)paramAsStr, (stringLength + 3) << 1))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - paramsLength[index] = stringLength + 2; - } - paramAsStr[0] = '\''; - xmemmove(¶mAsStr[1], stringChars, stringLength << 1); - paramAsStr[stringLength + 1] = '\''; - paramAsStr[stringLength + 2] = 0; - } - else // The parameter is null; - TC_CharP2JCharPBuf("null", 4, paramAsStr, true); - } - - return true; -} - -// juliana@230_30: reduced log files size. -/** - * Returns the sql used in this statement in a string buffer. If logging is disabled, returns the sql without the arguments. If logging is enabled, - * returns the real sql, filled with the arguments. Used only for the logger. - * - * @param context The thread context where the function is being executed. - * @param statement The prepared statement. - * @return the sql used in this statement as a StringBuffer object. - */ -TCObject toStringBuffer(Context context, TCObject statement) -{ - TRACE("toStringBuffer") - TCObject logSBuffer = litebaseConnectionClass->objStaticValues[2]; - - StringBuffer_count(logSBuffer) = 0; - if (OBJ_PreparedStatementStoredParams(statement)) // There are no parameters. - { - int16* paramsPos = getPreparedStatementParamsPos(statement); - JCharP sql = String_charsStart(OBJ_PreparedStatementSqlExpression(statement)); - JCharP* paramsAsStrs = getPreparedStatementParamsAsStrs(statement); - int32 storedParams = OBJ_PreparedStatementStoredParams(statement), - i = -1; - - // PREP: + string before the first '?'. - if (!TC_appendCharP(context, logSBuffer, "PREP: ") || !TC_appendJCharP(context, logSBuffer, sql, paramsPos[0])) - return null; - - // Concatenates each string part with the next parameter. - while (++i < storedParams && TC_appendJCharP(context, logSBuffer, paramsAsStrs[i], TC_JCharPLen(paramsAsStrs[i])) - && TC_appendJCharP(context, logSBuffer, sql + paramsPos[i] + 1, (paramsPos[i + 1] - paramsPos[i] - 1))); - - if (i < storedParams) - return null; - } - else - { - TCObject sql = OBJ_PreparedStatementSqlExpression(statement); - if (!TC_appendJCharP(context, logSBuffer, String_charsStart(sql), String_charsLen(sql))) - return null; - } - - return logSBuffer; -} - -// juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first execution. -/** - * Resets a where clause because the expression may change between runs of a prepared statement with a where clause. - * - * @param whereClause the were clause to be reseted. - * @param heap A heap to allocate the clone of the where clause expression tree. - */ -void resetWhereClause(SQLBooleanClause* whereClause, Heap heap) -{ - TRACE("resetWhereClause") - if (whereClause) // guich@552_37: it may be null - { - whereClause->appliedIndexesBooleanOp = whereClause->appliedIndexesCount = 0; - - // After the first use of this, the tree is nulled. So, a copy of it is gotten. - // guich@554_13: Use the expressionTreeBak as a condition instead of expressionTree (it should always be replaced after the first try). - whereClause->expressionTree = cloneTree(whereClause->expressionTreeBak, whereClause->expressionTree, heap); - - whereClause->resultSet = null; - } -} - -// juliana@226_14: corrected a bug that would make a prepared statement with group by not work correctly after the first execution. -/** - * Resets an order by or group by clause because the tableColIndex may change between runs of a prepared statement with a sort field. - * So, it is necessary to cache the tableColIndex of order by fields. - * - * @param orderByClause the order by clause to be reseted. - */ -void resetColumnListClause(SQLColumnListClause* columnListClause) -{ - TRACE("resetColumnListClause") - if (columnListClause) // It may be null. - { - int32 n = columnListClause->fieldsCount; - SQLResultSetField** fieldList = columnListClause->fieldList; - uint8* fieldTableColIndexesBak = columnListClause->fieldTableColIndexesBak; - - while (--n >= 0) - fieldList[n]->tableColIndex = fieldTableColIndexesBak[n]; - } -} - -/** - * Stores the null values of prepared statement in the table. - * - * @param table The Table used in the prepared statement. - * @param record The prepared statement record. - * @param storeNulls The prepared statement field that indicates if it is to store nulls in the parameters or not. - * @param paramDefined Indicates which parameters are defined. - * @param paramIndexes The parameters indexes. - * @param nValues The number of fields or values of the prepared statement. - * @param paramCount The number of parameters. - * @param isPreparedUpdateStmt Indicates if the prepared statement is an update prepared statement or not. - */ -void rearrangeNullsInTable(Table* table, SQLValue** record, uint8* storeNulls, uint8* paramDefined, uint8* paramIndexes, int32 nValues, - int32 paramCount) -{ - TRACE("rearrangeNullsInTable") - int32 length = nValues < paramCount? nValues : paramCount; - - // juliana@201_17: acessed an invalid if record[paramIndexes[length]] == null. - while (--length >= 0) - if (!paramDefined[length] && record[paramIndexes[length]]) - record[paramIndexes[length]]->isNull = true; - - xmemmove(table->storeNulls, storeNulls, NUMBEROFBYTES(table->columnCount)); -} - -/** - * Tests if the prepared statement or the driver where it was created is closed. - * - * @param p->obj[0] The prepared statement object. - * @throws IllegalStateException If the prepared statement or driver is closed. - */ -bool testPSClosed(NMParams params) -{ - TRACE("testPSClosed") - TCObject statement = params->obj[0]; - - if (OBJ_PreparedStatementDontFinalize(statement)) // Tests if the prepared statement is closed. - { - TC_throwExceptionNamed(params->currentContext, "java.lang.IllegalStateException", getMessage(ERR_PREPARED_STMT_CLOSED)); - return false; - } - if (OBJ_LitebaseDontFinalize(OBJ_PreparedStatementDriver(statement))) // The connection with Litebase can't be closed. - { - TC_throwExceptionNamed(params->currentContext, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - return false; - } - return true; -} diff --git a/LitebaseSDK/src/native/PreparedStatement.h b/LitebaseSDK/src/native/PreparedStatement.h deleted file mode 100644 index 88d00adcb6..0000000000 --- a/LitebaseSDK/src/native/PreparedStatement.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions to deal with important prepared statements. - */ - -#ifndef LITEBASE_PREPAREDSTATEMENT_H -#define LITEBASE_PREPAREDSTATEMENT_H - -/** - * Frees a prepared statement. - * - * @param unused Parameter for htFree(). - * @param statement The prepared statement to be freed. - */ -void freePreparedStatement(int32 unused, TCObject statement); - -/** - * Sets numeric parameters in a prepared statement. - * - * @param p->obj[0] The prepared statement. - * @param p->i32[0] The index of the parameter value to be set, starting from 0. - * @param p->i32[1] The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool psSetNumericParamValue(NMParams p, int32 type); - -/** - * Sets a string parameter in a prepared statement. - * - * @param context The thread context where the function is being executed. - * @param stmt The prepared statement object. - * @param string The string object to be inserted. - * @param index The parameter index. - * @param stringLength The string length. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool psSetStringParamValue(Context context, TCObject stmt, TCObject string, int32 index, int32 stringLength); - -/** - * Returns the sql used in this statement in a string buffer. If logging is disabled, returns the sql without the arguments. If logging is enabled, - * returns the real sql, filled with the arguments. Used only for the logger. - * - * @param context The thread context where the function is being executed. - * @param statement The prepared statement. - * @return the sql used in this statement as a StringBuffer object. - */ -TCObject toStringBuffer(Context context, TCObject statement); - -/** - * Resets a where clause because the expression may change between runs of a prepared statement with a where clause. - * - * @param whereClause the were clause to be reseted. - * @param heap A heap to allocate the clone of the where clause expression tree. - */ -void resetWhereClause(SQLBooleanClause* whereClause, Heap heap); - -/** - * Resets an order by or group by clause because the tableColIndex may change between runs of a prepared statement with a sort field. - * So, it is necessary to cache the tableColIndex of order by fields. - * - * @param orderByClause the order by clause to be reseted. - */ -void resetColumnListClause(SQLColumnListClause* columnListClause); - -/** - * Stores the null values of prepared statement in the table. - * - * @param table The Table used in the prepared statement. - * @param record The prepared statement record. - * @param storeNulls The prepared statement field that indicates if it is to store nulls in the parameters or not. - * @param paramDefined Indicates which parameters are defined. - * @param paramIndexes The parameters indexes. - * @param nValues The number of fields or values of the prepared statement. - * @param paramCount The number of parameters. - * @param isPreparedUpdateStmt Indicates if the prepared statement is an update prepared statement or not. - */ -void rearrangeNullsInTable(Table* table, SQLValue** record, uint8* storeNulls, uint8* paramDefined, uint8* paramIndexes, int32 nValues, - int32 paramCount); - -/** - * Tests if the prepared statement or the driver where it was created is closed. - * - * @param p->obj[0] The prepared statement object. - * @throws IllegalStateException If the prepared statement or driver is closed. - */ -bool testPSClosed(NMParams params); - -#endif diff --git a/LitebaseSDK/src/native/ResultSet.c b/LitebaseSDK/src/native/ResultSet.c deleted file mode 100644 index ab1bbcd425..0000000000 --- a/LitebaseSDK/src/native/ResultSet.c +++ /dev/null @@ -1,1476 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This module implements functions for fetching a set or rows resulting from a LitebaseConnection.executeQuery() method call. - * Here's an example: - * - *
    - * ResultSet rs = driver.executeQuery("select name, salary, age from person");
    - * while (rs.next())
    - *    Vm.debug(pad(rs.getString("name"), 32) + pad(rs.getString("salary"), 16) 
    - *                                                     + rs.getInt("age") + " years");
    - * 
    - * - * Result sets cannot be constructed directly; instead, you must issue a sql to the driver. - */ - -#include "ResultSet.h" - -/** - * Frees a result set structure. - * - * @param resultSet The resultSet to be freed. - */ -void freeResultSet(ResultSet* resultSet) -{ - TRACE("freeResultSet") - - // Only frees temporary tables. - // juliana@210_1: select * from table_name does not create a temporary table anymore. - if (resultSet->isTempTable) // juliana@223_14: solved possible memory problems. - freeTable(null, resultSet->table, false, false); - - // juliana@223_13: corrected a bug that could break the application when freeing a result set of a prepared statement. - // Only frees the select clause if it is not from a prepared statement, which might be used again. - if (resultSet->selectClause && !resultSet->isPrepared) - heapDestroy(resultSet->selectClause->heap); - - // juliana@263_3: corrected a bug where a new result set data could overlap an older result set data if both were related to the same table. - xfree(resultSet->allRowsBitmap); - - heapDestroy(resultSet->heap); // Frees the structure -} - -/** - * Creates a result set structure. - * - * @param table The table to be used by the result set, which can be temporary or not. - * @param whereClause The where clause to evaluate the records of the table to be returned to the user. - * @param heap The heap to allocate the result set structure. - * @return The result set created. - */ -ResultSet* createResultSet(Table* table, SQLBooleanClause* whereClause, Heap heap) -{ - TRACE("createResultSet") - ResultSet* resultSet = (ResultSet*)TC_heapAlloc(heap, sizeof(ResultSet)); - resultSet->pos = resultSet->answerCount = -1; // juliana@230_14 - resultSet->table = table; - resultSet->whereClause = whereClause; - resultSet->isTempTable = !*table->name; - return resultSet; -} - -/** - * Creates a simple result set structure and computes the indices. - * - * @param context The thread context where the function is being executed. - * @param table The table to be used by the result set, which can be temporary or not. - * @param whereClause The where clause to evaluate the records of the table to be returned to the user. - * @param heap The heap to allocate the result set structure. - * @return The result set created. - */ -ResultSet* createSimpleResultSet(Context context, Table* table, SQLBooleanClause* whereClause, Heap heap) -{ - TRACE("createSimpleResultSet") - ResultSet* resultSet; - resultSet = (ResultSet*)TC_heapAlloc(heap, sizeof(ResultSet)); - resultSet->pos = resultSet->indexRs = resultSet->answerCount = -1; // juliana@230_14 - resultSet->table = table; - resultSet->isTempTable = !*table->name; - resultSet->whereClause = whereClause; - resultSet->indexes = newIntVector(3, heap); - resultSet->heap = heap; - - // Tries to use the table indexes to generate a bitmap of the rows to be returned. - if (whereClause && !generateIndexedRowsMap(context, &resultSet, 1, table->numberComposedIndexes > 0, heap)) - { - freeResultSet(resultSet); - return null; - } - return resultSet; -} - -/** - * Creates a result set structure and computes the indices for the returning of a select statement. - * - * @param context The thread context where the function is being executed. - * @param table The table to be used by the result set, which can be temporary or not. - * @param whereClause The where clause to evaluate the records of the table to be returned to the user. - * @param heap The heap to allocate the result set structure. - * @return The result set created. - */ -ResultSet* createResultSetForSelect(Context context, Table* table, SQLBooleanClause* whereClause, Heap heap) -{ - TRACE("createResultSetForSelect") - ResultSet* resultSet; - - if (!(resultSet = createSimpleResultSet(context, table, whereClause, heap))) - return null; - - resultSet->pos = resultSet->answerCount = -1; // juliana@230_14 - resultSet->decimalPlaces = (int8*)TC_heapAlloc(resultSet->heap, resultSet->columnCount = table->columnCount); - xmemset(resultSet->decimalPlaces, -1, resultSet->columnCount); - return resultSet; -} - -/** - * Gets the next record of the result set. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @return true if there is a next record to go to in the result set; false, otherwise. - */ -bool resultSetNext(Context context, ResultSet* resultSet) -{ - TRACE("resultSetNext") - bool ret; - Table* table = resultSet->table; - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - uint8* rowsBitmap = resultSet->allRowsBitmap; - int32 rowCountLess1 = plainDB->rowCount - 1; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap) - { - int32 i = resultSet->pos; - - while (i++ < rowCountLess1) - if (isBitSet(rowsBitmap, i)) - { - if (plainRead(context, plainDB, resultSet->pos = i)) - { - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return true; - } - return false; - } - return false; - } - - if (table->deletedRowsCount > 0) - { - bool isDeleted = false; // Indicates if it was deleted. - int32 lastPos = resultSet->pos, // juliana@211_4: solved bugs with result set dealing. - value; - - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (resultSet->pos++ < rowCountLess1) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - if (!plainRead(context, plainDB, resultSet->pos)) - return false; - xmove4(&value, basbuf); - if (!(isDeleted = (value & ROW_ATTR_MASK) == ROW_ATTR_DELETED)) - break; - } - - if (resultSet->pos <= rowCountLess1 && !isDeleted) // Sets the position after the last record. - { - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return true; - } - - // juliana@211_4: solved bugs with result set dealing. - if (plainRead(context, plainDB, resultSet->pos = lastPos)) - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return false; - } - - if ((ret = resultSet->pos < rowCountLess1 && plainRead(context, plainDB, ++resultSet->pos))) - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return ret; -} - -/** - * Gets the previous record of the result set. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @return true if there is a next record to go to in the result set; false, otherwise. - */ -bool resultSetPrev(Context context, ResultSet* resultSet) -{ - TRACE("resultSetPrev") - bool ret; - Table* table = resultSet->table; - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - uint8* rowsBitmap = resultSet->allRowsBitmap; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (rowsBitmap) - { - int32 i = resultSet->pos; - - while (i-- > 0) - if (isBitSet(rowsBitmap, i)) - { - if (plainRead(context, plainDB, resultSet->pos = i)) - { - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return true; - } - return false; - } - return false; - } - - if (table->deletedRowsCount > 0) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - bool isDeleted = false; // Indicates if it was deleted. - int32 lastPos = resultSet->pos, // juliana@211_4: solved bugs with result set dealing. - value; - - while (resultSet->pos-- > 0) // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - { - if (!plainRead(context, plainDB, resultSet->pos)) - return false; - xmove4(&value, basbuf); - if (!(isDeleted = (value & ROW_ATTR_MASK) == ROW_ATTR_DELETED)) - break; - } - - if (resultSet->pos >= 0 && !isDeleted) // Sets the position after the last record. - { - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return true; - } - - // juliana@211_4: solved bugs with result set dealing. - if (plainRead(context, plainDB, resultSet->pos = lastPos)) - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return false; - } - - if ((ret = resultSet->pos > 0 && plainRead(context, plainDB, --resultSet->pos))) - xmemmove(table->columnNulls, basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - return ret; -} - -/** - * Given the column index (starting from 1), returns a short value that is represented by this column. Note that it is only possible to request - * this column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ -int32 rsGetShort(ResultSet* resultSet, int32 column) -{ - TRACE("rsGetShort") - int16 value; // juliana@227_18: corrected a possible insertion of a negative short column being recovered in the select as positive. - xmove2(&value, &resultSet->table->db.basbuf[resultSet->table->columnOffsets[column]]); - return value; -} - -/** - * Given the column index (starting from 1), returns an integer value that is represented by this column. Note that it is only possible to request - * this column as integer if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ -int32 rsGetInt(ResultSet* resultSet, int32 column) -{ - TRACE("rsGetInt") - int32 value; - Table* table = resultSet->table; - xmove4(&value, &table->db.basbuf[table->columnOffsets[column]]); - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (!column && *table->name) - value &= ROW_ID_MASK; - return value; -} - -/** - * Given the column index (starting from 1), returns a long value that is represented by this column. Note that it is only possible to request - * this column as long if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ -int64 rsGetLong(ResultSet* resultSet, int32 column) -{ - TRACE("rsGetLong") - int64 value; - xmove8(&value, &resultSet->table->db.basbuf[resultSet->table->columnOffsets[column]]); - return value; -} - -/** - * Given the column index (starting from 1), returns a float value that is represented by this column. Note that it is only possible to request - * this column as float if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ -float rsGetFloat(ResultSet* resultSet, int32 column) -{ - TRACE("rsGetFloat") - float value; - xmove4(&value, &resultSet->table->db.basbuf[resultSet->table->columnOffsets[column]]); - return value; -} - -/** - * Given the column index (starting from 1), returns a double value that is represented by this column. Note that it is only possible to request - * this column as double if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ -double rsGetDouble(ResultSet* resultSet, int32 column) -{ - TRACE("rsGetDouble") - double value; - READ_DOUBLE((uint8*)&value, &resultSet->table->db.basbuf[resultSet->table->columnOffsets[column]]); - return value; -} - -// juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. -// juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. -/** - * Given the column index (starting from 1), returns a char array that is represented by this column. Note that it is only possible to request - * this column as a char array if it was created as a string. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @param column The column index. - * @param value A SQLValue to hold the char array. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ -TCObject rsGetChars(Context context, ResultSet* resultSet, int32 column, SQLValue* value) -{ - TRACE("rsGetChars") - int32 length = 0, - position; - Table* table = resultSet->table; - PlainDB* plainDB = &table->db; - XFile* dbo; - TCObject object; - - // Fetches the string position in the .dbo of the disk table. - loadPlainDBAndPosition(&plainDB->basbuf[table->columnOffsets[column]], &plainDB, &position); - - nfSetPos(dbo = &plainDB->dbo, position); - if (position >= dbo->finalPos) - length = 0; - else if (!nfReadBytes(context, dbo, (uint8*)&length, 2)) - return null; - - if (length > table->columnSizes[column]) // juliana@270_22: solved a possible crash when the table is corrupted. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), table->name); - return null; - } - - // Creates the returning object and loads the string inside it. - if ((object = TC_createArrayObject(context, CHAR_ARRAY, length))) // guich@570_97: Checks often. - { - if (length) - { - value->asChars = (JCharP)ARRAYOBJ_START(object); - value->length = length; - - if (!loadString(context, plainDB, (JCharP)ARRAYOBJ_START(object), length)) - { - TC_setObjectLock(object, UNLOCKED); - return null; - } - } - return object; - } - return null; -} - -// juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. -/** - * Given the column index (starting from 1), fetches two integers values that are represented by this column. Note that it is only possible to - * request this column as date time if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @param The structure that will hold the two returned integers. - */ -void rsGetDateTimeValue(ResultSet* resultSet, int32 column, SQLValue* value) -{ - TRACE("rsGetInt") - Table* table = resultSet->table; - uint8* basbuf = table->db.basbuf; - int32 offset = table->columnOffsets[column]; - - xmove4(&value->asDate, &basbuf[offset]); - xmove4(&value->asTime, &basbuf[offset + 4]); -} - -// juliana@220_3: blobs are not loaded anymore in the temporary table when building result sets. -/** - * Given the column index (starting from 1), returns a byte array (blob) that is represented by this column. Note that it is only possible to request - * this column as a blob if it was created as a string. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ -TCObject rsGetBlob(Context context, ResultSet* resultSet, int32 column) -{ - TRACE("rsGetBlob") - int32 length, - position; - Table* table = resultSet->table; - PlainDB* plainDB = &table->db; - TCObject object; - - // Fetches the blob position in the .dbo of the disk table. - loadPlainDBAndPosition(&plainDB->basbuf[table->columnOffsets[column]], &plainDB, &position); - - nfSetPos(&plainDB->dbo, position); - if (position >= plainDB->dbo.finalPos) - length = 0; - else if (!nfReadBytes(context, &plainDB->dbo, (uint8*)&length, 4)) - return null; - - if (length > table->columnSizes[column]) // juliana@270_22: solved a possible crash when the table is corrupted. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), table->name); - return null; - } - - // guich@570_97: checks often. - // Creates the returning object and copies the blob to it. - if ((object = TC_createArrayObject(context, BYTE_ARRAY, length)) && nfReadBytes(context, &plainDB->dbo, ARRAYOBJ_START(object), length)) - return object; - - TC_setObjectLock(object, UNLOCKED); - return null; -} - -// juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. -// juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. -/** - * Given the column index (starting from 1), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @param column The column index. - * @param value A SQLValue to hold the char array. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ -TCObject rsGetString(Context context, ResultSet* resultSet, int32 column, SQLValue* value) -{ - TRACE("rsGetString") - Table* table = resultSet->table; - PlainDB* plainDB = &table->db; - uint8 *ptr = &plainDB->basbuf[table->columnOffsets[column]]; - switch (table->columnTypes[column]) - { - case SHORT_TYPE: - xmove2(&value->asShort, ptr); // juliana@227_18: corrected a possible insertion of a negative short column being recovered in the select as positive. - break; - case INT_TYPE: - xmove4(&value->asInt, ptr); - if (!column && *table->name) // juliana@213_4: rowid was not being returned correctly if the table was not temporary. - value->asInt &= ROW_ID_MASK; - break; - case LONG_TYPE: - xmove8(&value->asLong, ptr); - break; - case FLOAT_TYPE: - xmove4(&value->asFloat, ptr); - break; - case DOUBLE_TYPE: - READ_DOUBLE((uint8*)&value->asDouble, ptr); - break; - case DATE_TYPE: // rnovais@567_2 - xmove4(&value->asInt, ptr); - break; - case DATETIME_TYPE: // rnovais@_567_2 - xmove4(&value->asDate, ptr); - xmove4(&value->asTime, ptr + 4); - break; - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - { - int32 length = 0, - position; - XFile* dbo; - TCObject object; - - // Fetches the string position in the .dbo of the disk table. - loadPlainDBAndPosition(ptr, &plainDB, &position); - - nfSetPos(dbo = &plainDB->dbo, position); - if (position >= dbo->finalPos) - length = 0; - else if (!nfReadBytes(context, dbo, (uint8*)&length, 2)) - return null; - - if (length > table->columnSizes[column]) // juliana@270_22: solved a possible crash when the table is corrupted. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), table->name); - return null; - } - - // Creates the returning object and loads the string inside it. - if ((object = TC_createStringObjectWithLen(context, length))) // guich@570_97: check often - { - if (length) - { - value->asChars = String_charsStart(object); - - if (!loadString(context, plainDB, (JCharP)String_charsStart(object), value->length = length)) - { - TC_setObjectLock(object, UNLOCKED); - return null; - } - - } - } - return object; - } - } - return null; -} - -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -// juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. -/** - * Starting from the current cursor position, it reads all result set rows that are being requested. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param p->obj[0] The result set. - * @param p->retO receives a matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column - * elements of the first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is null. - * @param count The number of rows to be fetched, or -1 for all. - * @throws DriverException If the result set position is invalid. - * @throws IllegalArgumentException If count is less then -1. - */ -void getStrings(NMParams params, int32 count) // juliana@201_2: corrected a bug that would let garbage in the number of records parameter. -{ - TRACE("getStrings") - ResultSet* resultSet = getResultSetBag(*params->obj); - Table* table; - Context context = params->currentContext; - int32 position; - - if (testRSClosed(context, *params->obj)) // The driver and the result set can't be closed. - { - if ((position = resultSet->pos) >= 0 && position <= (table = resultSet->table)->db.rowCount - 1) // Invalid result set position. - { - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - TCObject* strings; - TCObject* matrixEntry; - TCObject result; - int8* columnTypes = table->columnTypes; - uint8* columnNulls0 = table->columnNulls; - SQLValue value; - bool notTemporary = resultSet->answerCount >= 0 || resultSet->isSimpleSelect; - SQLResultSetField** fields = resultSet->selectClause->fieldList; - SQLResultSetField* field; - - // juliana@211_4: solved bugs with result set dealing. - // juliana@211_3: the string matrix size can't take into consideration rows that are before the result set pointer. - int32 cols = resultSet->selectClause->fieldsCount, - validRecords = 0, - i, - column, - records = table->db.rowCount - resultSet->pos; // juliana@210_1: select * from table_name does not create a temporary table anymore. - - // juliana@210_1: select * from table_name does not create a temporary table anymore. - - if (count < -1) // juliana@211_4: solved bugs with result set dealing. - { - TC_throwExceptionNamed(context, "java.lang.IllegalArgumentException", getMessage(ERR_RS_INV_POS), count); - return; - } - - // Checks the ranges - if (count == -1) - count = 0xFFFFFFF; - count = MIN(count, records); - - // juliana@230_19: removed some possible memory problems with prepared statements and ResultSet.getStrings(). - if (!(params->retO = result = TC_createArrayObject(context,"[[java.lang.String", count)) || !count) // juliana@211_4: solved bugs with result set dealing. - { - TC_setObjectLock(result, UNLOCKED); - return; - } - matrixEntry = (TCObject*)ARRAYOBJ_START(params->retO); - - do - { - if (!(*matrixEntry = TC_createArrayObject(context, "[java.lang.String", cols))) // juliana@201_19: Does not consider rowid. - { - TC_setObjectLock(result, UNLOCKED); - return; - } - TC_setObjectLock(*matrixEntry, UNLOCKED); - - // We will hold the found objects in the native stack to avoid them being collected. - strings = (TCObject*)ARRAYOBJ_START(*(matrixEntry++)); - i = -1; - while (++i < cols) - { - field = fields[i]; - column = notTemporary? (field->parameter? field->parameter->tableColIndex : field->tableColIndex) : i; - - if (isBitUnSet(columnNulls0, column) && columnTypes[column] != BLOB_TYPE) - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - *strings = rsGetString(context, resultSet, column, &value); - - // juliana@270_31: Corrected bug of ResultSet.getStrings() don't working properly when there is a data function in the columns - // being fetched. - if (!(*strings) || field->isDataTypeFunction) - { - if (field->isDataTypeFunction) - { - rsApplyDataTypeFunction(params, &value, field, UNDEFINED_TYPE); - if (!(columnTypes[column] == CHARS_TYPE || columnTypes[column] == CHARS_NOCASE_TYPE)) - *strings++ = params->retO; - else - TC_setObjectLock(*strings++, UNLOCKED); - } - else - { - createString(params, &value, columnTypes[column], resultSet->decimalPlaces? resultSet->decimalPlaces[column] : -1); - *strings++ = params->retO; - } - } - else - TC_setObjectLock(*strings++, UNLOCKED); - if (params->currentContext->thrownException) - { - TC_setObjectLock(result, UNLOCKED); - return; - } - } - else - *strings++ = null; - } - validRecords++; // juliana@211_4: solved bugs with result set dealing. - } - while (--count && resultSetNext(context, resultSet)); - - TC_setObjectLock(params->retO = result, UNLOCKED); - if ((int32)ARRAYOBJ_LEN(result) > validRecords) // juliana@211_4: solved bugs with result set dealing. - { - TCObject matrix; - - matrixEntry = (TCObject*)ARRAYOBJ_START(params->retO); - if (!(matrix = TC_createArrayObject(context,"[[java.lang.String", validRecords))) - return; - xmemmove(ARRAYOBJ_START(matrix), matrixEntry, TSIZE * validRecords); - TC_setObjectLock(params->retO = matrix, UNLOCKED); - } - } - else - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_RS_INV_POS), position); - } -} - -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns a column value of the result set given its type and column index. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param type The type of the column. UNDEFINED must be used to return anything except for blobs as strings. - * @param p->retI receives an int or a short if type is INT or SHORY, respectively. - * @param p->retL receives a long if type is LONG. - * @param p->retD receives a float or a double if type is FLOAT or DOUBLE, respectively. - * @param p->retO receives a string, a character array, a blob, a date, or a time if type is UNDEFINED, CHARS, - * BLOB, DATE, or DATETIME. - * respectively. - */ -void rsGetByIndex(NMParams p, int32 type) -{ - TRACE("rsGetByIndex") - TCObject resultSet = p->obj[0]; - - if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - rsPrivateGetByIndex(p, type); -} - -// juliana@230_27: if a public method in now called when its object is already closed, now an IllegalStateException will be thrown instead of a -// DriverException. -/** - * Returns a column value of the result set given its type and column name. DATE will be returned as a single int. This function can't be used to - * return a DATETIME. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param type The type of the column. UNDEFINED must be used to return anything except for blobs as strings. - * @param p->retI receives an int or a short if type is INT or SHORY, respectively. - * @param p->retL receives a long if type is LONG. - * @param p->retD receives a float or a double if type is FLOAT or DOUBLE, respectively. - * @param p->retO receives a string, a character array or a blob if type is UNDEFINED, CHARS, or BLOB, - * respectively. - * @throws NullPointerException If the column name is null. - */ -void rsGetByName(NMParams p, int32 type) -{ - TRACE("rsGetByName") - TCObject resultSet = p->obj[0], - colName = p->obj[1]; - - // juliana@227_4: the connection where the result set was created can't be closed while using it. - if (!colName) - TC_throwNullArgumentException(p->currentContext, "colName"); - else if (testRSClosed(p->currentContext, resultSet)) // The driver and the result set can't be closed. - { - if ((p->i32[0] = TC_htGet32Inv(&getResultSetBag(resultSet)->intHashtable, identHashCode(colName)) + 1) >= 0) - rsPrivateGetByIndex(p, type); - else // juliana@266_2: corrected exception message when an unknown column name was passed to a ResultSet method. - TC_throwExceptionNamed(p->currentContext, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NAME), colName); - } -} - -/** - * Returns a column value of the result set given its type and column index. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param type The type of the column. UNDEFINED must be used to return anything except for blobs as strings. - * @param p->retI receives an int or a short if type is INT or SHORY, respectively. - * @param p->retL receives a long if type is LONG. - * @param p->retD receives a float or a double if type is FLOAT or DOUBLE, respectively. - * @param p->retO receives a string, a character array, a blob, a date, or a time if type is UNDEFINED, CHARS, - * BLOB, DATE, or DATETIME. - * respectively. - * @throws DriverException If the kind of return type asked is incompatible from the column definition type. - */ -void rsPrivateGetByIndex(NMParams p, int32 type) -{ - TRACE("rsGetByIndex") - ResultSet* rsBag = getResultSetBag(p->obj[0]); - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@227_14: corrected a DriverException not being thrown when fetching in some cases when trying to fetch data from an invalid result - // set column. - // juliana@210_1: select * from table_name does not create a temporary table anymore. - // juliana@201_23: the types must be compatible. - int32 col = *p->i32, - typeCol; - SQLResultSetField* field; - SQLValue value; - - if (!verifyRSState(p->currentContext, rsBag, col--)) - return; - - field = rsBag->selectClause->fieldList[col]; - if (rsBag->allRowsBitmap || rsBag->isSimpleSelect) - col = field->parameter? field->parameter->tableColIndex : field->tableColIndex; - - // juliana@227_13: corrected a DriverException not being thrown when issuing ResultSet.getChars() for a column that is not of CHARS, CHARS - // NOCASE, VARCHAR, or VARCHAR NOCASE. - typeCol = rsBag->table->columnTypes[col]; - - // juliana@270_28: now it is not allowed to fetch a string field in ResultSet with methods that aren't getString() or getChars(). - if (type != UNDEFINED_TYPE) - if (!(field->isDataTypeFunction && type == SHORT_TYPE && (typeCol == DATE_TYPE || typeCol == DATETIME_TYPE)) - && (typeCol != type && ((typeCol != CHARS_NOCASE_TYPE && typeCol != CHARS_TYPE) || (type != CHARS_NOCASE_TYPE && type != CHARS_TYPE)))) - { - TC_throwExceptionNamed(p->currentContext, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return; - } - - xmemzero(&value, sizeof(value)); - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - if (isBitUnSet(rsBag->table->columnNulls, col)) - { - switch (typeCol) - { - case SHORT_TYPE: - p->retI = value.asShort = rsGetShort(rsBag, col); - break; - case INT_TYPE: - p->retI = value.asInt = rsGetInt(rsBag, col); - break; - case LONG_TYPE: - p->retL = value.asLong = rsGetLong(rsBag, col); - break; - case FLOAT_TYPE: - p->retD = value.asFloat = rsGetFloat(rsBag, col); - break; - case DOUBLE_TYPE: - p->retD = value.asDouble = rsGetDouble(rsBag, col); - break; - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - if (type == CHARS_TYPE) - TC_setObjectLock(p->retO = rsGetChars(p->currentContext, rsBag, col, &value), UNLOCKED); - else - TC_setObjectLock(p->retO = rsGetString(p->currentContext, rsBag, col, &value), UNLOCKED); // STRING - break; - case DATE_TYPE: - value.asInt = rsGetInt(rsBag, col); - if (type == DATE_TYPE) - setDateObject(p, value.asInt); - break; - case DATETIME_TYPE: - rsGetDateTimeValue(rsBag, col, &value); - if (type == DATETIME_TYPE) - setTimeObject(p, value.asDate, value.asTime); - break; - case BLOB_TYPE: - if (type == BLOB_TYPE) - TC_setObjectLock(p->retO = rsGetBlob(p->currentContext, rsBag, col), UNLOCKED); - else - p->retO = null; - } - if (field->isDataTypeFunction) - rsApplyDataTypeFunction(p, &value, field, type); - else if (type == UNDEFINED_TYPE) - createString(p, &value, typeCol, rsBag->decimalPlaces? rsBag->decimalPlaces[col] : -1); - } - else - { - p->retD = 0; // Since this is a union, just assigns 0 to the widest type. - p->retO = null; // p->retO is not in the union. - } -} - -/** - * Given the column index (starting from 1), indicates if this column has a NULL. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives true if the value is SQL NULL; false, otherwise. - */ -void rsPrivateIsNull(NMParams params) -{ - ResultSet* rsBag = getResultSetBag(params->obj[0]); - - // juliana@227_14: corrected a DriverException not being thrown when fetching in some cases when trying to fetch data from an invalid result - // set column. - // juliana@210_1: select * from table_name does not create a temporary table anymore. - int32 column = params->i32[0]; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if (verifyRSState(params->currentContext, rsBag, column--)) - { - if (rsBag->allRowsBitmap || rsBag->isSimpleSelect) - { - SQLResultSetField* field = rsBag->selectClause->fieldList[column]; - column = field->parameter? field->parameter->tableColIndex : field->tableColIndex; - } - params->retI = isBitSet(rsBag->table->columnNulls, column); - } -} - -// juliana@230_28: if a public method receives an invalid argument, now an IllegalArgumentException will be thrown instead of a DriverException. -/** - * Verifies if the result set and the column index are valid. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param column The result set table column being searched. - * @return true if the the result set and the column index are valid; false, otherwise. - * @throws DriverException If the result set position is invalid. - * @throws IllegalArgumentException If the column index is invalid. - */ -bool verifyRSState(Context context, ResultSet* resultSet, int32 column) -{ - TRACE("verifyRSState") - int32 position = resultSet->pos; - if (position < 0 || position > resultSet->table->db.rowCount - 1) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_RS_INV_POS), position); - return false; - } - if (column <= 0 || column > resultSet->selectClause->fieldsCount) // Cols given by the user range from 1 to n. - { - TC_throwExceptionNamed(context, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_COLUMN_NUMBER), column); - return false; - } - return true; -} - - -/** - * Gets the next record of a result set. This function is to be used by the result sets created internally by the Litebase code, not by external - * result sets. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return true if there is a next record to go to in the result set; false, otherwise. - */ -bool getNextRecord(Context context, ResultSet* resultSet, Heap heap) -{ - TRACE("getNextRecord") - PlainDB* plainDB = &resultSet->table->db; - IntVector* rowsBitmap = &resultSet->rowsBitmap; - uint8* basbuf = plainDB->basbuf; - SQLBooleanClause* whereClause = resultSet->whereClause; - int32 rowCountLess1 = plainDB->rowCount - 1; - bool ret; - - if (rowsBitmap->size > 0) // Desired rows partially computed using the indexes? - { - int32 position; - - if (resultSet->pos < rowCountLess1) - { - if (!whereClause) - { - // juliana@227_7: solved a bug on delete when trying to delete a key from a column which has index and there are deleted rows with the - // same key. - // No WHERE clause. Just returns the rows marked in the bitmap. - while ((position = findNextBitSet(rowsBitmap, resultSet->pos + 1)) != -1 && position <= rowCountLess1 - && plainRead(context, plainDB, resultSet->pos = position)) - if (recordNotDeleted(basbuf)) - return true; - } - else - { - // With a remaining WHERE clause there are 2 situations. - // 1) The relationship between the bitmap and the WHERE clause is an AND relationship, and - // 2) The relationship between the bitmap and the WHERE clause is an OR relationship. - if (resultSet->rowsBitmapBoolOp == OP_BOOLEAN_AND) - { - // juliana@227_7: solved a bug on delete when trying to delete a key from a column which has index and there are deleted rows with the - // same key. - // AND case - walks through the bits that are set in the bitmap and checks if rows satisfies the where clause. - // juliana@230_22: solved a bug of not finding rows in a where clause with AND where only one of its sides does not use an index and - // there are deleted rows. - while ((position = findNextBitSet(rowsBitmap, resultSet->pos + 1)) != -1 && position <= rowCountLess1) - if (plainRead(context, plainDB, resultSet->pos = position)) - { - if (recordNotDeleted(basbuf)) - { - if ((ret = sqlBooleanClauseSatisfied(context, whereClause, resultSet, heap)) == -1) - return false; - if (ret) - return true; - } - } - else - return false; - } - else - { - // OR case - walks through all records. If the corresponding bit is set in the bitmap, do not need to evaluate WHERE clause. - // Otherwise, checks if the row satisifies the WHERE clause. - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (resultSet->pos < rowCountLess1 && plainRead(context, plainDB, ++resultSet->pos)) - { - if (IntVectorisBitSet(rowsBitmap, resultSet->pos)) - return true; - - if (recordNotDeleted(basbuf)) - { - if ((ret = sqlBooleanClauseSatisfied(context, whereClause, resultSet, heap)) == -1) - return false; - if (ret) - return true; - } - } - } - } - } - } - else - { - // If the where clause exists, it needs to be satisfied. - // juliana@201_27: solved a bug in next() and prev() that would happen after doing a delete from table_name. - while (resultSet->pos < rowCountLess1 && plainRead(context, plainDB, ++resultSet->pos)) - if (recordNotDeleted(basbuf)) - { - if (whereClause) - { - if ((ret = sqlBooleanClauseSatisfied(context, whereClause, resultSet, heap)) == -1) - return false; - if (ret) - return true; - } - else - return true; - } - } - return false; -} - -// rnovais@567_2 -/** - * Formats a int date into a date string according with the device formatting settings. - * - * @param buffer The buffer where the date will be stored as a string. - * @param intDate An integer representing a time. - */ -void formatDate(CharP buffer, int32 intDate) -{ - TRACE("formatDate") - int32 day = intDate % 100, - month = (intDate /= 100) % 100, - year = intDate / 100, - value1, - value2, - value3; - TCSettings settings = TC_getSettingsPtr(); - char dateSeparator = *settings->dateSeparatorPtr; - int32 dateFormat = *settings->dateFormatPtr; - - if (dateFormat == DATE_MDY) - { - value1 = month; - value2 = day; - value3 = year; - } - else if (dateFormat == DATE_YMD) - { - value1 = year; - value2 = month; - value3 = day; - } - else - { - value1 = day; - value2 = month; - value3 = year; - } - buffer = zeroPad(buffer, value1, 10); - *buffer++ = dateSeparator; - buffer = zeroPad(buffer, value2, 10); - *buffer++ = dateSeparator; - buffer = zeroPad(buffer, value3, 1000); - *buffer = 0; -} - -/** - * Formats an int time into a time according with the device formatting settings. - * - * @param buffer The buffer where the date will be stored as a string. - * @param intDate An integer representing a time. - */ -void formatTime(CharP buffer, int32 intTime) -{ - TRACE("formatTime") - int32 mills = intTime % 1000, - second = (intTime /= 1000) % 100, - minute = (intTime /= 100) % 100, - hour = (intTime / 100) % 100; - TCSettings settings = TC_getSettingsPtr(); - int32 useAmPm = !*settings->is24HourPtr; - char timeSeparator = *settings->timeSeparatorPtr; - int32 h; - - if (useAmPm) - { - if (hour == 0 || hour == 12) - h = 12; - else - h = hour < 12? hour : (hour - 12); - } - else - h = hour; - - buffer = zeroPad(buffer, h, 10); - *buffer++ = timeSeparator; - buffer = zeroPad(buffer, minute, 10); - *buffer++ = timeSeparator; - buffer = zeroPad(buffer, second, 10); - *buffer++ = timeSeparator; - buffer = zeroPad(buffer, mills, 100); - if (useAmPm) - { - *buffer++ = ' '; - if (hour >= 12) - *buffer++ = 'P'; - else - *buffer++ = 'A'; - *buffer++ = 'M'; - } - *buffer = 0; -} - -/** - * Pads a numeric string with zeros on the left to format dates and times. - * - * @param buffer The string which stores a date or a time. - * @param value The date or time part to be inserted in the string. - * @param order The decimal order of the value being inserted in the string. - * @return The buffer string address offset by the number of decimal orders. - */ -CharP zeroPad(CharP buffer, int32 value, uint32 order) // rnovais@567_2 -{ - TRACE("zeroPad") - while (order) - { - *buffer++ = ((value / order) % 10) + '0'; - order /= 10; - } - return buffer; -} - -/** - * Calculates the hash code of a string object. - * - * @param stringObj The string object. - * @return The hash code of the string object. - */ -int32 identHashCode(TCObject stringObj) -{ - TRACE("identHashCode") - int32 hash = 0, - value; - uint32 length = String_charsLen(stringObj); - JCharP chars = String_charsStart(stringObj); - while (length--) - { - value = (int32)*chars++; - if (value >= (int32)'A' && value <= (int32)'Z') // guich@104 - value += 32; - hash = (hash << 5) - hash + value; // It was 31 * hash. - } - return hash; -} - -// juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. -/** - * Applies a function when fetching data from the result set. - * - * @param params->currentContext The thread context where the function is being executed. - * @param params->retO The returned data as a string if the user wants the table data in this format. - * @param value The value where the function will be applied. - * @param field The field where the function is being applied. - * @param type The type of the field being returned. - */ -void rsApplyDataTypeFunction(NMParams params, SQLValue* value, SQLResultSetField* field, int32 type) -{ - TRACE("rsApplyDataTypeFunction") - - applyDataTypeFunction(value, field->sqlFunction, field->parameter->dataType); - switch (field->sqlFunction) - { - case FUNCTION_DT_YEAR: - case FUNCTION_DT_MONTH: - case FUNCTION_DT_DAY: - case FUNCTION_DT_HOUR: - case FUNCTION_DT_MINUTE: - case FUNCTION_DT_SECOND: - case FUNCTION_DT_MILLIS: - { - if (type == UNDEFINED_TYPE) - { - IntBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_int2str(value->asShort, buffer), -1), - UNLOCKED); - } - else - params->retI = value->asShort; - break; - } - case FUNCTION_DT_ABS: - switch (field->parameter->dataType) - { - case SHORT_TYPE: - { - if (type == UNDEFINED_TYPE) - { - IntBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_int2str(value->asShort, buffer), -1), - UNLOCKED); - } - else - params->retI = value->asShort; - break; - } - case INT_TYPE: - { - if (type == UNDEFINED_TYPE) - { - IntBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_int2str(value->asInt, buffer), -1), - UNLOCKED); - } - else - params->retI = value->asInt; - break; - } - case LONG_TYPE: - { - if (type == UNDEFINED_TYPE) - { - LongBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_long2str(value->asLong, buffer), -1), - UNLOCKED); - } - else - params->retL = value->asLong; - break; - } - case FLOAT_TYPE: - { - if (type == UNDEFINED_TYPE) - { - DoubleBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_double2str(value->asFloat, value->length, - buffer), -1), UNLOCKED); - } - else - params->retD = value->asFloat; - break; - } - case DOUBLE_TYPE: - { - if (type == UNDEFINED_TYPE) - { - DoubleBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_double2str(value->asDouble, value->length, - buffer), -1), UNLOCKED); - } - else - params->retD = value->asDouble; - } - - } - } -} - -// juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. -/** - * Creates a string to return to the user. - * - * @param params->currentContext The thread context where the function is being executed. - * @param params->retO The returned data as a string if the user wants the table data in this format. - * @param value The value where the function will be applied. - * @param type The type of the value being returned to the user. - * @param decimalPlaces The number of decimal places if the value is a floating point number. - */ -void createString(NMParams params, SQLValue* value, int32 type, int32 decimalPlaces) -{ - TRACE("createString") - - switch (type) - { - case SHORT_TYPE: - { - IntBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_int2str(value->asShort, buffer), -1), UNLOCKED); - break; - } - case INT_TYPE: - { - IntBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_int2str(value->asInt, buffer), -1), UNLOCKED); - break; - } - case LONG_TYPE: - { - LongBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_long2str(value->asLong, buffer), -1), UNLOCKED); - break; - } - case FLOAT_TYPE: - { - DoubleBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_double2str((double)value->asFloat, decimalPlaces, - buffer), -1), UNLOCKED); - break; - } - case DOUBLE_TYPE: - { - DoubleBuf buffer; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, TC_double2str(value->asDouble, decimalPlaces, - buffer), -1), UNLOCKED); - break; - } - case DATE_TYPE: - { - DateBuf dateBuf; - formatDate(dateBuf, value->asInt); - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, dateBuf, 10), UNLOCKED); - break; - } - case DATETIME_TYPE: // rnovais@_567_2 - { - DateTimeBuf dateTimeBuf; - formatDate(dateTimeBuf, value->asDate); - formatTime(&dateTimeBuf[11], value->asTime); - dateTimeBuf[10] = ' '; - TC_setObjectLock(params->retO = TC_createStringObjectFromCharP(params->currentContext, dateTimeBuf, 23), UNLOCKED); - } - } -} - -/** - * Loads the physical table where a string or blob is stored and its position in the .dbo file. - * - * @param buffer A buffer where is stored the string position in the result set dbo. - * @param plainDB The result set plainDB, which will become the physical one if the query uses a temporary table. - * @param position The position of the string or blob in the physical dbo. - */ -void loadPlainDBAndPosition(uint8* buffer, PlainDB** plainDB, int32* position) -{ - TRACE("loadPlainDBAndPosition") - - xmove4(position, buffer); // Loads the string or blob position in the .dbo. - - // Loads the string or blob length in the .dbo. - if (!*(*plainDB)->name) // juliana@210_1: select * from table_name does not create a temporary table anymore. - { - // juliana@212_5: correct a bug that would crash the program when issuing ResultSet.getChars() with a select which does not use a temporary table. - uint8* ptrStr = (*plainDB)->dbo.fbuf + *position; - - xmove4(position, ptrStr); - ptrStr += 4; - xmoveptr(plainDB, ptrStr); - } -} - -/** - * Tests if the result set or the driver where it was created is closed. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set object. - * @throws IllegalStateException If the result set or driver is closed. - */ -bool testRSClosed(Context context, TCObject resultSet) -{ - TRACE("testRSClosed") - if (OBJ_ResultSetDontFinalize(resultSet)) // Prepared Statement Closed. - { - TC_throwExceptionNamed(context, "java.lang.IllegalStateException", getMessage(ERR_RESULTSET_CLOSED)); - return false; - } - if (OBJ_LitebaseDontFinalize(getResultSetBag(resultSet)->driver)) // The connection with Litebase can't be closed. - { - TC_throwExceptionNamed(context, "java.lang.IllegalStateException", getMessage(ERR_DRIVER_CLOSED)); - return false; - } - return true; -} - -// juliana@253_3: added methods to return the primary key columns of a table. -/** - * Returns a table used in a select given its name. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param tableName The table name. - * @return The table with the given name or null if an exception occurs. - * @throws DriverException if the given table name is not used in the select. - */ -Table* getTableRS(Context context, ResultSet* resultSet, CharP tableName) -{ - SQLResultSetField** fields = resultSet->selectClause->fieldList; - int32 i = resultSet->selectClause->fieldsCount; - - // The table name must be used in the select. - while (--i >= 0 && (!fields[i]->tableName || xstrcmp(fields[i]->tableName, tableName))); - if (i == -1) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_NAME_NOT_FOUND), tableName); - return null; - } - return getTable(context, resultSet->driver, tableName); -} - -//juliana@253_4: added methods to return the default value of a column. -/** - * Gets the default value of a column. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param tableName The name of the table. - * @param index The column index. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If the column index is of a column of type BLOB. - */ -TCObject getDefault(Context context, ResultSet* resultSet, CharP tableName, int32 index) -{ - Table* table; - - if ((table = getTable(context, resultSet->driver, tableName))) - { - int32 type = table->columnTypes[index]; - SQLValue* value = table->defaultValues[index]; - DoubleBuf buffer; - CharP valueCharP = ""; - - if (!value) // No default value, returns null. - return null; - - switch (type) - { - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - { - TCObject string = TC_createStringObjectWithLen(context, value->length); - if (!string) - return null; - xmemmove(String_charsStart(string), value->asChars, value->length << 1); - return string; - } - case SHORT_TYPE: - { - valueCharP = TC_int2str(value->asShort, buffer); - break; - } - case INT_TYPE: - { - valueCharP = TC_int2str(value->asInt, buffer); - break; - } - case LONG_TYPE: - { - valueCharP = TC_long2str(value->asLong, buffer); - break; - } - case FLOAT_TYPE: - { - valueCharP = TC_double2str(value->asFloat, -1, buffer); - break; - } - case DOUBLE_TYPE: - { - valueCharP = TC_double2str(value->asDouble, -1, buffer); - break; - } - case DATE_TYPE: - { - int32 dateInt = value->asDate; - - xstrprintf(valueCharP = buffer, "%04d/%02d/%02d", dateInt / 10000, dateInt / 100 % 100, dateInt % 100); - break; - } - case DATETIME_TYPE: - { - int32 dateInt = value->asDate, - timeInt = value->asTime; - - xstrprintf(valueCharP = buffer, "%04d/%02d/%02d", dateInt / 10000, dateInt / 100 % 100, dateInt % 100); - xstrprintf(&buffer[11], "%02d:%02d:%02d:%03d", timeInt / 10000000, timeInt / 100000 % 100, timeInt / 1000 % 100, timeInt % 1000); - buffer[10] = ' '; - break; - } - case BLOB_TYPE: // Blob can't be used with default. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_BLOB_STRING)); - return null; - } - } - - // Types that are not string. - return TC_createStringObjectFromCharP(context, valueCharP, -1); - } - - return null; -} diff --git a/LitebaseSDK/src/native/ResultSet.h b/LitebaseSDK/src/native/ResultSet.h deleted file mode 100644 index ca6ee9fd56..0000000000 --- a/LitebaseSDK/src/native/ResultSet.h +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This module declares functions for fetching a set or rows resulting from a LitebaseConnection.executeQuery() method call. - * Here's an example: - * - *
    - * ResultSet rs = driver.executeQuery("select name, salary, age from person");
    - * while (rs.next())
    - *    Vm.debug(pad(rs.getString("name"), 32) + pad(rs.getString("salary"), 16) 
    - *                                                     + rs.getInt("age") + " years");
    - * 
    - * - * Result sets cannot be constructed directly; instead, you must issue a sql to the driver. - */ - -#ifndef LITEBASE_RESULTSET_H -#define LITEBASE_RESULTSET_H - -#include "Litebase.h" - -/** - * Frees a result set structure. - * - * @param resultSet The resultSet to be freed. - */ -void freeResultSet(ResultSet* resultSet); - -/** - * Creates a result set structure. - * - * @param table The table to be used by the result set, which can be temporary or not. - * @param whereClause The where clause to evaluate the records of the table to be returned to the user. - * @param heap The heap to allocate the result set structure. - * @return The result set created. - */ -ResultSet* createResultSet(Table* table, SQLBooleanClause* whereClause, Heap heap); - -/** - * Creates a simple result set structure and computes the indices. - * - * @param context The thread context where the function is being executed. - * @param table The table to be used by the result set, which can be temporary or not. - * @param whereClause The where clause to evaluate the records of the table to be returned to the user. - * @param heap The heap to allocate the result set structure. - * @return The result set created. - */ -ResultSet* createSimpleResultSet(Context context, Table* table, SQLBooleanClause* whereClause, Heap heap); - -/** - * Creates a result set structure and computes the indices for the returning of a select statement. - * - * @param context The thread context where the function is being executed. - * @param table The table to be used by the result set, which can be temporary or not. - * @param whereClause The where clause to evaluate the records of the table to be returned to the user. - * @param heap The heap to allocate the result set structure. - * @return The result set created. - */ -ResultSet* createResultSetForSelect(Context context, Table* table, SQLBooleanClause* whereClause, Heap heap); - -/** - * Gets the next record of the result set. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @return true if there is a next record to go to in the result set; false, otherwise. - */ -bool resultSetNext(Context context, ResultSet* resultSet); - -/** - * Gets the previous record of the result set. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @return true if there is a next record to go to in the result set; false, otherwise. - */ -bool resultSetPrev(Context context, ResultSet* resultSet); - -/** - * Given the column index (starting from 1), returns a short value that is represented by this column. Note that it is only possible to request - * this column as short if it was created with this precision or if the data being fetched is the result of a DATE or DATETIME SQL function. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ -int32 rsGetShort(ResultSet* resultSet, int32 column); - -/** - * Given the column index (starting from 1), returns an integer value that is represented by this column. Note that it is only possible to request - * this column as integer if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ -int32 rsGetInt(ResultSet* resultSet, int32 column); - -/** - * Given the column index (starting from 1), returns a long value that is represented by this column. Note that it is only possible to request - * this column as long if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0. - */ -int64 rsGetLong(ResultSet* resultSet, int32 column); - -/** - * Given the column index (starting from 1), returns a float value that is represented by this column. Note that it is only possible to request - * this column as float if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ -float rsGetFloat(ResultSet* resultSet, int32 column); - -/** - * Given the column index (starting from 1), returns a double value that is represented by this column. Note that it is only possible to request - * this column as double if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is 0.0. - */ -double rsGetDouble(ResultSet* resultSet, int32 column); - -/** - * Given the column index (starting from 1), returns a char array that is represented by this column. Note that it is only possible to request - * this column as a char array if it was created as a string. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @param column The column index. - * @param value A SQLValue to hold the char array. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ -TCObject rsGetChars(Context context, ResultSet* resultSet, int32 column, SQLValue* value); - -/** - * Given the column index (starting from 1), fetches two integers values that are represented by this column. Note that it is only possible to - * request this column as date time if it was created with this precision. - * - * @param resultSet The result set to be searched. - * @param column The column index. - * @param The structure that will hold the two returned integers. - */ -void rsGetDateTimeValue(ResultSet* resultSet, int32 column, SQLValue* value); - -/** - * Given the column index (starting from 1), returns a byte array (blob) that is represented by this column. Note that it is only possible to request - * this column as a blob if it was created as a string. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @param column The column index. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ -TCObject rsGetBlob(Context context, ResultSet* resultSet, int32 column); - -/** - * Given the column index (starting from 1), returns a string that is represented by this column. Any column type can be returned as a string. - * Double/float values formatting will use the precision set with the setDecimalPlaces() method. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set to be searched. - * @param column The column index. - * @param value A SQLValue to hold the char array. - * @return The column value; if the value is SQL NULL, the value returned is null. - */ -TCObject rsGetString(Context context, ResultSet* resultSet, int32 column, SQLValue* value); - -/** - * Starting from the current cursor position, it reads all result set rows that are being requested. first(), last(), - * prev(), or next() must be used to set the current position, but not beforeFirst() or - * afterLast(). It doesn't return BLOB values. null is returned in their places instead. - * - * @param p->obj[0] The result set. - * @param p->retO receives a matrix, where String[0] is the first row, and String[0][0], String[0][1]... are the column - * elements of the first row. Returns null if here's no more element to be fetched. Double/float values will be formatted using the - * setDecimalPlaces() settings. If the value is SQL NULL or a blob, the value returned is null. - * @param count The number of rows to be fetched, or -1 for all. - * @throws DriverException If the result set or the driver is closed, or the result set position is invalid. - * @throws IllegalArgumentException If count is less then -1. - */ -void getStrings(NMParams p, int32 count); // juliana@201_2: corrected a bug that would let garbage in the number of records parameter. - -/** - * Returns a column value of the result set given its type and column index. DATE will be returned as a single int. This function can't be used to - * return a DATETIME. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param type The type of the column. UNDEFINED must be used to return anything except for blobs as strings. - * @param p->retI receives an int or a short if type is INT or SHORY, respectively. - * @param p->retL receives a long if type is LONG. - * @param p->retD receives a float or a double if type is FLOAT or DOUBLE, respectively. - * @param p->retO receives a string, a character array or a blob if type is UNDEFINED, CHARS, or BLOB, - * respectively. - */ -void rsGetByIndex(NMParams p, int32 type); - -/** - * Returns a column value of the result set given its type and column name. DATE will be returned as a single int. This function can't be used to - * return a DATETIME. - * - * @param p->obj[0] The result set. - * @param p->obj[1] The column name. - * @param type The type of the column. UNDEFINED must be used to return anything except for blobs as strings. - * @param p->retI receives an int or a short if type is INT or SHORY, respectively. - * @param p->retL receives a long if type is LONG. - * @param p->retD receives a float or a double if type is FLOAT or DOUBLE, respectively. - * @param p->retO receives a string, a character array or a blob if type is UNDEFINED, CHARS, or BLOB, - * respectively. - * @throws NullPointerException If the column name is null. - */ -void rsGetByName(NMParams p, int32 type); - -/** - * Returns a column value of the result set given its type and column index. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param type The type of the column. UNDEFINED must be used to return anything except for blobs as strings. - * @param p->retI receives an int or a short if type is INT or SHORY, respectively. - * @param p->retL receives a long if type is LONG. - * @param p->retD receives a float or a double if type is FLOAT or DOUBLE, respectively. - * @param p->retO receives a string, a character array, a blob, a date, or a time if type is UNDEFINED, CHARS, - * BLOB, DATE, or DATETIME. - * respectively. - * @throws DriverException If the kind of return type asked is incompatible from the column definition type. - */ -void rsPrivateGetByIndex(NMParams p, int32 type); - -/** - * Given the column index (starting from 1), indicates if this column has a NULL. - * - * @param p->obj[0] The result set. - * @param p->i32[0] The column index. - * @param p->retI receives true if the value is SQL NULL; false, otherwise. - */ -void rsPrivateIsNull(NMParams params); - -/** - * Verifies if the result set and the column index are valid. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param column The result set table column being searched. - * @return true if the the result set and the column index are valid; false, otherwise. - * @throws DriverException If the result set position is invalid. - * @throws IllegalArgumentException If the column index is invalid. - */ -bool verifyRSState(Context context, ResultSet* resultSet, int32 column); - -/** - * Gets the next record of a result set. This function is to be used by the result sets created internally by the Litebase code, not by external - * result sets. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return true if there is a next record to go to in the result set; false, otherwise. - */ -bool getNextRecord(Context context, ResultSet* resultSet, Heap heap); - -// rnovais@567_2 -/** - * Formats a int date into a date string according with the device formatting settings. - * - * @param buffer The buffer where the date will be stored as a string. - * @param intDate An integer representing a date. - */ -void formatDate(CharP buffer, int32 intDate); - -/** - * Formats an int time into a time according with the device formatting settings. - * - * @param buffer The buffer where the date will be stored as a string. - * @param intDate An integer representing a time. - */ -void formatTime(CharP buffer, int32 intTime); - -/** - * Pads a numeric string with zeros on the left to format dates and times. - * - * @param buffer The string which stores a date or a time. - * @param value The date or time part to be inserted in the string. - * @param order The decimal order of the value being inserted in the string. - * @return The buffer string address offset by the number of decimal orders. - */ -CharP zeroPad(CharP buffer, int32 value, uint32 order); - -/** - * Calculates the hash code of a string object. - * - * @param stringObj The string object. - * @return The hash code of the string object. - */ -int32 identHashCode(TCObject stringObj); - -/** - * Applies a function when fetching data from the result set. - * - * @param params->currentContext The thread context where the function is being executed. - * @param params->retO The returned data as a string if the user wants the table data in this format. - * @param value The value where the function will be applied. - * @param field The field where the function is being applied. - * @param type The type of the field being returned. - */ -void rsApplyDataTypeFunction(NMParams params, SQLValue* value, SQLResultSetField* field, int32 type); - -/** - * Creates a string to return to the user. - * - * @param params->currentContext The thread context where the function is being executed. - * @param params->retO The returned data as a string if the user wants the table data in this format. - * @param value The value where the function will be applied. - * @param type The type of the value being returned to the user. - * @param decimalPlaces The number of decimal places if the value is a floating point number. - */ -void createString(NMParams params, SQLValue* value, int32 type, int32 decimalPlaces); - -/** - * Loads the physical table where a string or blob is stored and its position in the .dbo file. - * - * @param buffer A buffer where is stored the string position in the result set dbo. - * @param plainDB The result set plainDB, which will become the physical one if the query uses a temporary table. - * @param position The position of the string or blob in the physical dbo. - */ -void loadPlainDBAndPosition(uint8* buffer, PlainDB** plainDB, int32* position); - -/** - * Tests if the result set or the driver where it was created is closed. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set object. - * @throws IllegalStateException If the result set or driver is closed. - */ -bool testRSClosed(Context context, TCObject resultSet); - -/** - * Returns a table used in a select given its name. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param tableName The table name. - * @return The table with the given name or null if an exception occurs. - * @throws DriverException if the given table name is not used in the select. - */ -Table* getTableRS(Context context, ResultSet* resultSet, CharP tableName); - -/** - * Gets the default value of a column. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set. - * @param tableName The name of the table. - * @param index The column index. - * @return The default value of the column as a string or null if there is no default value. - * @throws DriverException If the column index is of a column of type BLOB. - */ -TCObject getDefault(Context context, ResultSet* resultSet, CharP tableName, int32 index); - -#endif diff --git a/LitebaseSDK/src/native/SQLValue.c b/LitebaseSDK/src/native/SQLValue.c deleted file mode 100644 index 92cc380ecf..0000000000 --- a/LitebaseSDK/src/native/SQLValue.c +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines functions to deal with a a value which can be inserted in a column of a table. - */ - -#include "SQLValue.h" - -/** - * Creates an array of SQLValues. - * - * @param count The array size. - * @param heap The heap to allocate the array. - * @return The SQLValue array. - */ -SQLValue** newSQLValues(int32 count, Heap heap) -{ - TRACE("newSQLValues") - SQLValue** values; - - values = (SQLValue**)TC_heapAlloc(heap, count * TSIZE); - while (--count >= 0) - values[count] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - return values; -} - -// rnovais@568_10 rnovais@570_5 -// null values must be handled before. If the value is null, this function can't be called. -/** - * Applies the function on the value. - * - * @param value The value where the function will be applied. - * @param sqlFunction The code of the function to be applied. - * @param paramDataType The data type of the parameter. - */ -void applyDataTypeFunction(SQLValue* value, int32 sqlFunction, int32 paramDataType) -{ - TRACE("applyDataTypeFunction") - switch (sqlFunction) - { - case FUNCTION_DT_YEAR: - value->asShort = (int16)(value->asInt / 10000); - break; - case FUNCTION_DT_MONTH: - value->asShort = (int16)(value->asInt / 100 % 100); - break; - case FUNCTION_DT_DAY: - value->asShort = (int16)(value->asInt % 100); - break; - case FUNCTION_DT_HOUR: - value->asShort = (int16)(value->asTime / 10000000); - break; - case FUNCTION_DT_MINUTE: - value->asShort = (int16)(value->asTime / 100000 % 100); - break; - case FUNCTION_DT_SECOND: - value->asShort = (int16)(value->asTime / 1000 % 100); - break; - case FUNCTION_DT_MILLIS: - value->asShort = (int16)(value->asTime % 1000); - break; - case FUNCTION_DT_ABS: // rnovais@570_1 - switch (paramDataType) // rnovais@570_5 - { - case SHORT_TYPE: - if (value->asShort < 0) - value->asShort = -value->asShort; - break; - case INT_TYPE: - if (value->asInt < 0) - value->asInt = -value->asInt; - break; - case LONG_TYPE: - if (value->asLong < 0) - value->asLong = -value->asLong; - break; - case FLOAT_TYPE: - if (value->asFloat < 0.0F) - value->asFloat = -value->asFloat; - break; - case DOUBLE_TYPE: - if (value->asDouble < 0.0) - value->asDouble = -value->asDouble; - break; - } - break; - case FUNCTION_DT_UPPER: // rnovais@570_1 - { - int32 length = value->length; - JChar* asChars = value->asChars; - while (--length >= 0) - { - *asChars = TC_JCharToUpper(*asChars); - asChars++; - } - break; - } - case FUNCTION_DT_LOWER: // rnovais@570_1 - { - int32 length = value->length; - JChar* asChars = value->asChars; - while (--length >= 0) - { - *asChars = TC_JCharToLower(*asChars); - asChars++; - } - } - } -} - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * Compares 2 values. - * - * @param context The thread context where the function is being executed. - * @param value1 The fist value used in the comparison. - * @param value1 The second value used in the comparison. - * @param type The types of the values being compared. - * @param isNull1 Indicates if the value being compared is null. - * @param isNull2 Indicates if the value being compared against is null. - * @param plainDB the plainDB of a table if it is necessary to load a string. - * @return 0 if the values are identical; a positive number if the value being compared is greater than the one being compared against; otherwise, - * a negative number. - */ -int32 valueCompareTo(Context context, SQLValue* value1, SQLValue* value2, int32 type, bool isNull1, bool isNull2, PlainDB* plainDB) -{ - TRACE("valueCompareTo") - - if (isNull1 || isNull2) // A null value is always considered to be the greatest value. - return (isNull1 == isNull2)? 0 : (isNull1? 1 : -1); - - switch (type) - { - case CHARS_NOCASE_TYPE: - case CHARS_TYPE: - if (!value2->length && plainDB) - { - int32 length = 0; - - nfSetPos(&plainDB->dbo, value2->asInt); - if (!nfReadBytes(context, &plainDB->dbo, (uint8*)&length, 2) || !loadString(context, plainDB, value2->asChars, value2->length = length)) - return false; - value2->asChars[length] = 0; - } - return str16CompareTo(value1->asChars, value2->asChars, value1->length, value2->length, type == CHARS_NOCASE_TYPE); - - case SHORT_TYPE: - return value1->asShort - value2->asShort; - case DATE_TYPE: // rnovais@567_2 - case INT_TYPE: - return value1->asInt - value2->asInt; - - case LONG_TYPE: - { - int64 ret = value1->asLong - value2->asLong; - return (ret == 0)? 0 : (ret > 0)? 1 : -1; - } - case FLOAT_TYPE : - { - float ret = value1->asFloat - value2->asFloat; - return (ret == 0.0)? 0 : (ret > 0)? 1 : -1; - } - case DOUBLE_TYPE: - { - double ret = value1->asDouble - value2->asDouble; - return (ret == 0.0)? 0 : (ret > 0)? 1 : -1; - } - case DATETIME_TYPE: // rnovais@567_2 rnovais@570_10 - { - int32 ret = value1->asDate - value2->asDate; - return (ret == 0)? value1->asTime - value2->asTime : ret; - } - } - return 0; -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Checks if applyDataTypeFunction() correctly applies the data type functions. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(applyDataTypeFunction) -{ - SQLValue value; - int32 hour = 0, - minute = 0, - second = 0, - time, - day = 1, - month = 1, - year = 1000, - date; - bool changed = false; - int64 asLong; - double asDouble; - char bufferChar[27]; - JChar bufferJChar[27]; - UNUSED(currentContext) - - while (hour < 24) // Tests time functions. - { - getNextTime(&hour, &minute, &second); - - // hour - value.asTime = time = 1000 * (int32)getTimeLong(0, 0, 0, hour, minute, second); - applyDataTypeFunction(&value, FUNCTION_DT_HOUR, -1); - ASSERT2_EQUALS(I32, hour, value.asShort); - - // minute - value.asTime = time; - applyDataTypeFunction(&value, FUNCTION_DT_MINUTE, -1); - ASSERT2_EQUALS(I32, minute, value.asShort); - - // second - value.asTime = time; - applyDataTypeFunction(&value, FUNCTION_DT_SECOND, -1); - ASSERT2_EQUALS(I32, second, value.asShort); - - // millis - value.asTime = time; - applyDataTypeFunction(&value, FUNCTION_DT_MILLIS, -1); - ASSERT2_EQUALS(I32, 0, value.asShort); - } - - while (year < 3000) // Tests date functions. - { - getNextDate(&year, &month, &day); - - // year - value.asInt = date = year * 10000 + month * 100 + day; - applyDataTypeFunction(&value, FUNCTION_DT_YEAR, -1); - ASSERT2_EQUALS(I32, year, value.asShort); - - // month - value.asInt = date; - applyDataTypeFunction(&value, FUNCTION_DT_MONTH, -1); - ASSERT2_EQUALS(I32, month, value.asShort); - - // day - value.asInt = date; - applyDataTypeFunction(&value, FUNCTION_DT_DAY, -1); - ASSERT2_EQUALS(I32, day, value.asShort); - } - - // Tests ABS(short). - date = -32768; - while (date++ < 32767) - { - value.asShort = date; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, SHORT_TYPE); - ASSERT2_EQUALS(I32, date < 0? -date : date, value.asShort); - } - - // Tests ABS(int). - date = -2147483646; - while ((date += 131072) < 2147483646) - { - if (date > 0) - changed = true; - if (changed && date < 0) - break; - value.asInt = date; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, INT_TYPE); - ASSERT2_EQUALS(I32, date < 0? -date : date, value.asInt); - } - - // Tests ABS(long). - changed = false; - asLong = -9223372036854775807L; - while ((asLong += 274877906944L) < 9223372036854775807L) - { - if (asLong > 0) - changed = true; - if (changed && asLong < 0) - break; - value.asLong = asLong; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, LONG_TYPE); - ASSERT2_EQUALS(I64, asLong < 0? -asLong : asLong, value.asLong); - } - - // Tests ABS(float). - asDouble = MIN_FLOAT_VALUE; - while ((asDouble *= 10.0) < MAX_FLOAT_VALUE) - { - // positive - value.asFloat = (float)asDouble; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, FLOAT_TYPE); - ASSERT2_EQUALS(Dbl, (float)asDouble, value.asFloat); - - // negative - value.asFloat = -(float)asDouble; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, FLOAT_TYPE); - ASSERT2_EQUALS(Dbl, (float)asDouble, value.asFloat); - } - - // Tests ABS(double). - asDouble = 4.9E-324; - while ((asDouble *= 10.0) < 1.7976931348623157E308) - { - // positive - value.asDouble = asDouble; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, DOUBLE_TYPE); - ASSERT2_EQUALS(Dbl, asDouble, value.asDouble); - - // negative - value.asDouble = -asDouble; - applyDataTypeFunction(&value, FUNCTION_DT_ABS, DOUBLE_TYPE); - ASSERT2_EQUALS(Dbl, asDouble, value.asDouble); - } - - // Tests UPPER() and LOWER(). - xmemzero(bufferChar, 27); - date = 'A' - 1; - value.asChars = bufferJChar; - while (++date <= 'Z') - { - // LOWER - xmemset(bufferChar, date, value.length = time = date - 'A' + 1); - TC_CharP2JCharPBuf(bufferChar, time, bufferJChar, true); - applyDataTypeFunction(&value, FUNCTION_DT_LOWER, -1); - while (--time >= 0) - ASSERT2_EQUALS(I32, date + 32, bufferJChar[time]); - xmemset(bufferChar, date + 32, time = date - 'A' + 1); - TC_CharP2JCharPBuf(bufferChar, time, bufferJChar, true); - applyDataTypeFunction(&value, FUNCTION_DT_LOWER, -1); - while (--time >= 0) - ASSERT2_EQUALS(I32, date + 32, bufferJChar[time]); - - // UPPER - xmemset(bufferChar, date, value.length = time = date - 'A' + 1); - TC_CharP2JCharPBuf(bufferChar, time, bufferJChar, true); - applyDataTypeFunction(&value, FUNCTION_DT_UPPER, -1); - while (--time >= 0) - ASSERT2_EQUALS(I32, date, bufferJChar[time]); - xmemset(bufferChar, date + 32, time = date - 'A' + 1); - TC_CharP2JCharPBuf(bufferChar, time, bufferJChar, true); - applyDataTypeFunction(&value, FUNCTION_DT_UPPER, -1); - while (--time >= 0) - ASSERT2_EQUALS(I32, date, bufferJChar[time]); - } - -finish : ; -} - -/** - * Checks if newSQLValues() correctly creates an array of SQLValues. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(newSQLValues) -{ - Heap heap = heapCreate(); - int32 i = 256, - j; - SQLValue** sqlValues; - UNUSED(currentContext) - - IF_HEAP_ERROR(heap) - { - heapDestroy(heap); - TEST_FAIL(tc, "OutOfMemoryError"); - goto finish; - } - - while ((i -= 4) >= 0) // Creates arrays of SQLValues of various sizes. - { - sqlValues = newSQLValues(j = i, heap); - - while (--j >= 0) - { - ASSERT1_EQUALS(Null, sqlValues[j]->asChars); - ASSERT1_EQUALS(Null, sqlValues[j]->asBlob); - ASSERT2_EQUALS(I32, 0, sqlValues[j]->length); - ASSERT2_EQUALS(I32, 0, sqlValues[j]->isNull); - ASSERT2_EQUALS(I16, 0, sqlValues[j]->asShort); - ASSERT2_EQUALS(I32, 0, sqlValues[j]->asInt); - ASSERT2_EQUALS(I64, 0, sqlValues[j]->asLong); - ASSERT2_EQUALS(Dbl, 0, sqlValues[j]->asFloat); - ASSERT2_EQUALS(Dbl, 0, sqlValues[j]->asDouble); - ASSERT2_EQUALS(I32, 0, sqlValues[j]->asDate); - ASSERT2_EQUALS(I32, 0, sqlValues[j]->asTime); - } - } - - heapDestroy(heap); - -finish: ; -} - -/** - * Tests if valueCompareTo correctly compares SQLValues. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(valueCompareTo) -{ - SQLValue value1, - value2; - bool changed = false; - int32 asInt, - length; - int64 asLong; - double asDouble; - char bufferChar[27]; - JChar bufferJChar1[27], - bufferJChar2[27]; - - // Tests when one of the values is null. - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, null, null, -1, true, true, null)); - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, null, null, -1, true, false, null)); - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, null, null, -1, false, true, null)); - - // Tests short comparison. - asInt = -32768; - while (++asInt < 32767) - { - value1.asShort = asInt; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, SHORT_TYPE, false, false, null)); - value2.asShort = asInt + 1; - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, SHORT_TYPE, false, false, null)); - value2.asShort = asInt - 1; - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, SHORT_TYPE, false, false, null)); - } - - // Tests int comparison. - asInt = -2147483647; - while ((asInt += 131072) < 2147483647) - { - if (asInt > 0) - changed = true; - if (changed && asInt < 0) - break; - value1.asInt = asInt; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, INT_TYPE, false, false, null)); - value2.asInt = asInt + 1; - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, INT_TYPE, false, false, null)); - value2.asInt = asInt - 1; - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, INT_TYPE, false, false, null)); - } - - // Tests long comparison. - changed = false; - asLong = -9223372036854775807; - while ((asLong += 274877906944L) < 9223372036854775807) - { - if (asLong > 0) - changed = true; - if (changed && asLong < 0) - break; - value1.asLong = asLong; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, LONG_TYPE, false, false, null)); - value2.asLong = asLong + 1; - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, LONG_TYPE, false, false, null)); - value2.asLong = asLong - 1; - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, LONG_TYPE, false, false, null)); - } - - // Tests float comparison. - asDouble = MIN_FLOAT_VALUE; - while ((asDouble *= 10.0) < MAX_FLOAT_VALUE) - { - // positive - value1.asFloat = (float)asDouble; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, FLOAT_TYPE, false, false, null)); - value2.asFloat = (float)(asDouble * 10.0); - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, FLOAT_TYPE, false, false, null)); - value2.asFloat = (float)(asDouble / 10.0); - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, FLOAT_TYPE, false, false, null)); - - // negative - value1.asFloat = -(float)asDouble; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, FLOAT_TYPE, false, false, null)); - value2.asFloat = -(float)(asDouble * 10.0); - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, FLOAT_TYPE, false, false, null)); - value2.asFloat = -(float)(asDouble / 10.0); - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, FLOAT_TYPE, false, false, null)); - } - - // Tests double comparison. - asDouble = MIN_DOUBLE_VALUE; - while ((asDouble *= 10.0) < 1.7976931348623157E308) - { - // positive - value1.asDouble = asDouble; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, DOUBLE_TYPE, false, false, null)); - value2.asDouble = asDouble * 10.0; - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, DOUBLE_TYPE, false, false, null)); - value2.asDouble = asDouble / 10.0; - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, DOUBLE_TYPE, false, false, null)); - - // negative - value1.asDouble = -asDouble; - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value1, DOUBLE_TYPE, false, false, null)); - value2.asDouble = -asDouble * 10.0; - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, DOUBLE_TYPE, false, false, null)); - value2.asDouble = -asDouble / 10.0; - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value1, &value2, DOUBLE_TYPE, false, false, null)); - } - - // Tests CHARS and CHARS NOCASE comparison. - xmemzero(bufferChar, 27); - asInt = 'A' - 1; - value1.asChars = bufferJChar1; - value2.asChars = bufferJChar2; - while (++asInt <= 'Z') - { - xmemset(bufferChar, asInt, length = value1.length = value2.length = asInt - 'A' + 1); - TC_CharP2JCharPBuf(bufferChar, length, bufferJChar1, true); - TC_CharP2JCharPBuf(bufferChar, length, bufferJChar2, true); - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value2, CHARS_TYPE, false, false, null)); - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value2, CHARS_NOCASE_TYPE, false, false, null)); - applyDataTypeFunction(&value2, FUNCTION_DT_LOWER, -1); - ASSERT2_EQUALS(I32, -32, valueCompareTo(currentContext, &value1, &value2, CHARS_TYPE, false, false, null)); - ASSERT2_EQUALS(I32, +32, valueCompareTo(currentContext, &value2, &value1, CHARS_TYPE, false, false, null)); - ASSERT2_EQUALS(I32, 0, valueCompareTo(currentContext, &value1, &value2, CHARS_NOCASE_TYPE, false, false, null)); - applyDataTypeFunction(&value2, FUNCTION_DT_UPPER, -1); - value2.length--; - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, CHARS_TYPE, false, false, null)); - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value2, &value1, CHARS_TYPE, false, false, null)); - ASSERT2_EQUALS(I32, 1, valueCompareTo(currentContext, &value1, &value2, CHARS_NOCASE_TYPE, false, false, null)); - ASSERT2_EQUALS(I32, -1, valueCompareTo(currentContext, &value2, &value1, CHARS_NOCASE_TYPE, false, false, null)); - } - -finish : ; -} - -/** - * Gets the next time instant, increasing second by one and adjusting the other time values. - * - * @param hour The hour. - * @param minute The minute. - * @param second The second. - */ -void getNextTime(int32* hour, int32* minute, int32* second) -{ - if ((*second)++ == 60) - { - (*second) = 0; - - if ((*minute)++ == 60) - { - *minute = 0; - (*hour)++; - } - } -} - -/** - * Gets the next day, increasing day by one and adjusting the other date values. It takes lap years into consideration. - * - * @param year The year. - * @param month The month. - * @param day The day. - */ -void getNextDate(int32* year, int32* month, int32* day) -{ - (*day)++; - if (monthDays[*month - 1] < *day && !(*day == 29 && *month == 2 && (*year % 400 == 0 || (*year % 100 != 0 && *year % 4 == 0)))) - { - *day = 1; - if ((*month)++ > 12) - { - *month = 1; - (*year)++; - } - } -} - -#endif diff --git a/LitebaseSDK/src/native/SQLValue.h b/LitebaseSDK/src/native/SQLValue.h deleted file mode 100644 index a7aa592dbc..0000000000 --- a/LitebaseSDK/src/native/SQLValue.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions to deal with a a value which can be inserted in a column of a table. - */ - -#ifndef LITEBASE_SQLVALUE_H -#define LITEBASE_SQLVALUE_H - -#include "Litebase.h" - -/** - * Creates an array of SQLValues. - * - * @param count The array size. - * @param heap The heap to allocate the array. - * @return The SQLValue array. - */ -SQLValue** newSQLValues(int32 count, Heap heap); - -/** - * Applies the function on the value. - * - * @param value The value where the function will be applied. - * @param sqlFunction The code of the function to be applied. - * @param paramDataType The data type of the parameter. - */ -void applyDataTypeFunction(SQLValue* value, int32 sqlFunction, int32 paramDataType); - -/** - * Compares 2 values. - * - * @param context The thread context where the function is being executed. - * @param value1 The fist value used in the comparison. - * @param value1 The second value used in the comparison. - * @param type The types of the values being compared. - * @param isNull1 Indicates if the value being compared is null. - * @param isNull2 Indicates if the value being compared against is null. - * @param plainDB the plainDB of a table if it is necessary to load a string. - * @return 0 if the values are identical; a positive number if the value being compared is greater than the one being compared against; otherwise, - * a negative number. - */ -int32 valueCompareTo(Context context, SQLValue* value1, SQLValue* value2, int32 type, bool isNull1, bool isNull2, PlainDB* plainDB); - -#ifdef ENABLE_TEST_SUITE - -/** - * Checks if applyDataTypeFunction() correctly applies the data type functions. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_applyDataTypeFunction(TestSuite* testSuite, Context currentContext); - -/** - * Checks if newSQLValues() correctly creates an array of SQLValues. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_newSQLValues(TestSuite* testSuite, Context currentContext); - -/** - * Tests if valueCompareTo correctly compares SQLValues. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_valueCompareTo(TestSuite* testSuite, Context currentContext); - -/** - * Gets the next time instant, increasing second by one and adjusting the other time values. - * - * @param hour The hour. - * @param minute The minute. - * @param second The second. - */ -void getNextTime(int32* hour, int32* minute, int32* second); - -/** - * Gets the next day, increasing day by one and adjusting the other date values. - * - * @param year The year. - * @param month The month. - * @param day The day. - */ -void getNextDate(int32* year, int32* month, int32* day); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/TCVMLib.c b/LitebaseSDK/src/native/TCVMLib.c deleted file mode 100644 index 22f7bc8aa1..0000000000 --- a/LitebaseSDK/src/native/TCVMLib.c +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Implements the function used to load the pointers to TotalCross functions used by Litebase and delcares the function names. - */ - -#include "TCVMLib.h" - -#if defined (darwin) || defined (ANDROID) -#define GETPROCADDRESS(x) x -#else -#define GETPROCADDRESS(x) TC_getProcAddress(null, #x) -#endif - -/** - * Initializes the pointers to TotalCross functions used by Litebase. - */ -void initTCVMLib() -{ - TC_tiF_create_sii = TC_getProcAddress(null, "tiF_create_sii"); - TC_CharP2JCharP = GETPROCADDRESS(CharP2JCharP); - TC_CharP2JCharPBuf = GETPROCADDRESS(CharP2JCharPBuf); - TC_CharP2TCHARPBuf = GETPROCADDRESS(CharP2TCHARPBuf); - TC_CharPToLower = GETPROCADDRESS(CharPToLower); - TC_JCharP2CharP = GETPROCADDRESS(JCharP2CharP); - TC_JCharP2CharPBuf = GETPROCADDRESS(JCharP2CharPBuf); - TC_JCharP2TCHARPBuf = GETPROCADDRESS(JCharP2TCHARPBuf); - TC_JCharPEqualsJCharP = GETPROCADDRESS(JCharPEqualsJCharP); - TC_JCharPEqualsIgnoreCaseJCharP = GETPROCADDRESS(JCharPEqualsIgnoreCaseJCharP); - TC_JCharPHashCode = GETPROCADDRESS(JCharPHashCode); - TC_JCharPIndexOfJChar = GETPROCADDRESS(JCharPIndexOfJChar); - TC_JCharPLen = GETPROCADDRESS(JCharPLen); - TC_JCharToLower = GETPROCADDRESS(JCharToLower); - TC_JCharToUpper = GETPROCADDRESS(JCharToUpper); - TC_TCHARP2CharPBuf = GETPROCADDRESS(TCHARP2CharPBuf); - TC_alert = GETPROCADDRESS(alert); - TC_appendCharP = GETPROCADDRESS(appendCharP); // juliana@230_30 - TC_appendJCharP = GETPROCADDRESS(appendJCharP); // juliana@230_30 - TC_areClassesCompatible = GETPROCADDRESS(areClassesCompatible); - TC_createArrayObject = GETPROCADDRESS(createArrayObject); - TC_createObject = GETPROCADDRESS(createObject); - TC_createStringObjectFromCharP = GETPROCADDRESS(createStringObjectFromCharP); - TC_createStringObjectFromTCHARP = GETPROCADDRESS(createStringObjectFromTCHARP);; - TC_createStringObjectWithLen = GETPROCADDRESS(createStringObjectWithLen); - TC_debug = GETPROCADDRESS(debug); - TC_double2str = GETPROCADDRESS(double2str); - TC_executeMethod = GETPROCADDRESS(executeMethod); - TC_getApplicationId = GETPROCADDRESS(getApplicationId); - TC_getAppPath = GETPROCADDRESS(getAppPath); - //TC_getDataPath = GETPROCADDRESS(getDataPath); - TC_getDateTime = GETPROCADDRESS(getDateTime); - TC_getErrorMessage = GETPROCADDRESS(getErrorMessage); - TC_getSettingsPtr = GETPROCADDRESS(getSettingsPtr); - TC_getTimeStamp = GETPROCADDRESS(getTimeStamp); - TC_hashCode = GETPROCADDRESS(hashCode); - TC_hashCodeFmt = GETPROCADDRESS(hashCodeFmt); - TC_heapAlloc = GETPROCADDRESS(heapAlloc); - TC_heapDestroyPrivate = GETPROCADDRESS(heapDestroyPrivate); - TC_hstrdup = GETPROCADDRESS(hstrdup); - TC_htFree = GETPROCADDRESS(htFree); - TC_htFreeContext = GETPROCADDRESS(htFreeContext); - TC_htGet32 = GETPROCADDRESS(htGet32); - TC_htGet32Inv = GETPROCADDRESS(htGet32Inv); - TC_htGetPtr = GETPROCADDRESS(htGetPtr); - TC_htNew = GETPROCADDRESS(htNew); - TC_htPut32 = GETPROCADDRESS(htPut32); - TC_htPut32IfNew = GETPROCADDRESS(htPut32IfNew); - TC_htPutPtr = GETPROCADDRESS(htPutPtr); - TC_htRemove = GETPROCADDRESS(htRemove); - TC_int2CRID = GETPROCADDRESS(int2CRID); - TC_int2str = GETPROCADDRESS(int2str); - TC_listFiles = GETPROCADDRESS(listFiles); - TC_loadClass = GETPROCADDRESS(loadClass); - TC_long2str = GETPROCADDRESS(long2str); - TC_privateHeapCreate = GETPROCADDRESS(privateHeapCreate); - TC_privateHeapSetJump = GETPROCADDRESS(privateHeapSetJump); - TC_privateXfree = GETPROCADDRESS(privateXfree); - TC_privateXmalloc = GETPROCADDRESS(privateXmalloc); - TC_privateXrealloc = GETPROCADDRESS(privateXrealloc); - TC_setObjectLock = GETPROCADDRESS(setObjectLock); - TC_str2double = GETPROCADDRESS(str2double); - TC_str2int = GETPROCADDRESS(str2int); - TC_str2long = GETPROCADDRESS(str2long); - TC_throwExceptionNamed = GETPROCADDRESS(throwExceptionNamed); - TC_throwNullArgumentException = GETPROCADDRESS(throwNullArgumentException); - TC_toLower = GETPROCADDRESS(toLower); - TC_trace = GETPROCADDRESS(trace); - TC_validatePath = GETPROCADDRESS(validatePath); // juliana@214_1 -#ifdef ENABLE_MEMORY_TEST - TC_getCountToReturnNull = GETPROCADDRESS(getCountToReturnNull); - TC_setCountToReturnNull = GETPROCADDRESS(setCountToReturnNull); -#endif -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if the TotalCross functions were loaded successfully. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(initTCVMLib) -{ - UNUSED(currentContext) - ASSERT1_EQUALS(NotNull, TC_CharP2JCharP); - ASSERT1_EQUALS(NotNull, TC_CharP2JCharPBuf); - ASSERT1_EQUALS(NotNull, TC_CharP2TCHARPBuf); - ASSERT1_EQUALS(NotNull, TC_CharPToLower); - ASSERT1_EQUALS(NotNull, TC_JCharP2CharP); - ASSERT1_EQUALS(NotNull, TC_JCharP2CharPBuf); - ASSERT1_EQUALS(NotNull, TC_JCharP2TCHARPBuf); - ASSERT1_EQUALS(NotNull, TC_JCharPEqualsJCharP); - ASSERT1_EQUALS(NotNull, TC_JCharPEqualsIgnoreCaseJCharP); - ASSERT1_EQUALS(NotNull, TC_JCharPHashCode); - ASSERT1_EQUALS(NotNull, TC_JCharPIndexOfJChar); - ASSERT1_EQUALS(NotNull, TC_JCharPLen); - ASSERT1_EQUALS(NotNull, TC_JCharToLower); - ASSERT1_EQUALS(NotNull, TC_JCharToUpper); - ASSERT1_EQUALS(NotNull, TCHARP2CharPBuf); - ASSERT1_EQUALS(NotNull, TC_alert); - ASSERT1_EQUALS(NotNull, TC_createArrayObject); - ASSERT1_EQUALS(NotNull, TC_createObject); - ASSERT1_EQUALS(NotNull, TC_createStringObjectFromCharP); - ASSERT1_EQUALS(NotNull, TC_createStringObjectFromTCHARP); - ASSERT1_EQUALS(NotNull, TC_createStringObjectWithLen); - ASSERT1_EQUALS(NotNull, TC_debug); - ASSERT1_EQUALS(NotNull, TC_double2str); - ASSERT1_EQUALS(NotNull, TC_executeMethod); - ASSERT1_EQUALS(NotNull, TC_getApplicationId); - ASSERT1_EQUALS(NotNull, TC_getAppPath); - ASSERT1_EQUALS(NotNull, TC_getDataPath); - ASSERT1_EQUALS(NotNull, TC_getDateTime); - ASSERT1_EQUALS(NotNull, TC_getErrorMessage); - ASSERT1_EQUALS(NotNull, TC_getSettingsPtr); - ASSERT1_EQUALS(NotNull, TC_getTimeStamp); - ASSERT1_EQUALS(NotNull, TC_hashCode); - ASSERT1_EQUALS(NotNull, TC_hashCodeFmt); - ASSERT1_EQUALS(NotNull, TC_heapAlloc); - ASSERT1_EQUALS(NotNull, TC_heapDestroyPrivate); - ASSERT1_EQUALS(NotNull, TC_hstrdup); - ASSERT1_EQUALS(NotNull, TC_htFree); - ASSERT1_EQUALS(NotNull, TC_htFreeContext); - ASSERT1_EQUALS(NotNull, TC_htGet32); - ASSERT1_EQUALS(NotNull, TC_htGet32Inv); - ASSERT1_EQUALS(NotNull, TC_htGetPtr); - ASSERT1_EQUALS(NotNull, TC_htNew); - ASSERT1_EQUALS(NotNull, TC_htPut32); - ASSERT1_EQUALS(NotNull, TC_htPut32IfNew); - ASSERT1_EQUALS(NotNull, TC_htPutPtr); - ASSERT1_EQUALS(NotNull, TC_htRemove); - ASSERT1_EQUALS(NotNull, TC_int2CRID); - ASSERT1_EQUALS(NotNull, TC_int2str); - ASSERT1_EQUALS(NotNull, TC_listFiles); - ASSERT1_EQUALS(NotNull, TC_loadClass); - ASSERT1_EQUALS(NotNull, TC_long2str); - ASSERT1_EQUALS(NotNull, TC_privateHeapCreate); - ASSERT1_EQUALS(NotNull, TC_privateHeapSetJump); - ASSERT1_EQUALS(NotNull, TC_privateXfree); - ASSERT1_EQUALS(NotNull, TC_privateXmalloc); - ASSERT1_EQUALS(NotNull, TC_privateXrealloc); - ASSERT1_EQUALS(NotNull, TC_setObjectLock); - ASSERT1_EQUALS(NotNull, TC_str2double); - ASSERT1_EQUALS(NotNull, TC_str2int); - ASSERT1_EQUALS(NotNull, TC_str2long); - ASSERT1_EQUALS(NotNull, TC_throwExceptionNamed); - ASSERT1_EQUALS(NotNull, TC_throwNullArgumentException); - ASSERT1_EQUALS(NotNull, TC_tiF_create_sii); - ASSERT1_EQUALS(NotNull, TC_toLower); - ASSERT1_EQUALS(NotNull, TC_trace); - ASSERT1_EQUALS(NotNull, TC_validatePath); // juliana@214_1 - -#ifdef ENABLE_MEMORY_TEST - ASSERT1_EQUALS(NotNull, TC_getCountToReturnNull); - ASSERT1_EQUALS(NotNull, TC_setCountToReturnNull); -#endif - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/TCVMLib.h b/LitebaseSDK/src/native/TCVMLib.h deleted file mode 100644 index b68e1fd95f..0000000000 --- a/LitebaseSDK/src/native/TCVMLib.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the function used to load the pointers to TotalCross functions used by Litebase. - */ - -#ifndef LITEBASE_TCVMLIB_H -#define LITEBASE_TCVMLIB_H - -#include "Litebase.h" - -/** - * Initializes the pointers to TotalCross functions used by Litebase. - */ -void initTCVMLib(void); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if the TotalCross functions were loaded successfully. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_initTCVMLib(TestSuite* testSuite, Context currentContext);// TCVMLib_test.h - -#endif - -#endif diff --git a/LitebaseSDK/src/native/Table.c b/LitebaseSDK/src/native/Table.c deleted file mode 100644 index 434f67a4cd..0000000000 --- a/LitebaseSDK/src/native/Table.c +++ /dev/null @@ -1,3287 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -// juliana@253_5: removed .idr files from all indices and changed its format. -/** - * Declares functions to manipulate table structures. - */ - -#include "Table.h" - -/** - * Verifies if the index already exists. - * - * @param context The thread context where the function is being executed. - * @param table The table whose index is to be created. - * @param columnNumbers The columns that are part of this index. - * @param indexCount The number of columns of the index. - * @return 0 for simple indices. For composed index, if there was this same index, it returns the negative number of the this old one; otherwise, - * it returns the new number. - * @throws AlreadyCreatedException If an index already exists. - */ -int32 verifyIfIndexAlreadyExists(Context context, Table* table, uint8* columnNumbers, int32 indexCount) -{ - TRACE("verifyIfIndexAlreadyExists") - int32 idx = -1, - i; - if (indexCount == 1) // Simple index. - { - if (table->columnIndexes[idx = *columnNumbers]) - { - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_INDEX_ALREADY_CREATED), table->columnNames[idx]); - return -1; - } - return 0; - } - else // Composed index. - { - ComposedIndex* currCompIndex; - uint8* columns; - int32 size = i = table->numberComposedIndexes, - j; - - if (!size) // First index number. - return 1; - - while (--i >= 0) - { - currCompIndex = table->composedIndexes[i]; - columns = currCompIndex->columns; - j = currCompIndex->numberColumns; - - // juliana@253_2: corrected a bug if a composed index with less columns were created after one with more columns. - if (j == indexCount) - { - while (--j >= 0 && columnNumbers[j] == columns[j]); - - if (j < 0) - { - // Builds the exception message. - char errorMsg[1024]; - CharP* columnNames = table->columnNames; - - xstrcpy(errorMsg, columnNames[columnNumbers[j = 0]]); - while (++j < indexCount) - { - xstrcat(errorMsg, ", "); - xstrcat(errorMsg, columnNames[columnNumbers[j]]); - } - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_INDEX_ALREADY_CREATED), errorMsg); - return -1; - } - } - } - return size + 1; - } -} - -/** - * Drops an index. - * - * @param context The thread context where the function is being executed. - * @param table The table whose index is to dropped. - * @param column The column of the index dropped. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the column does not have an index. - */ -bool driverDropIndex(Context context, Table* table, int32 column) -{ - TRACE("driverDropIndex") // Column does not have an index. - Index* index = table->columnIndexes[column]; - - if (!index) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_DOESNOT_HAVE_AN_INDEX), table->columnNames[column]); - return false; - } - - if (!indexRemove(context, index)) // Deletes the index of this table. - return false; - table->columnIndexes[column] = null; // Already freed. - table->columnAttrs[column] &= ATTR_COLUMN_HAS_NO_INDEX; // Deletes the INDEX bit from the attributes. - - // Saves the meta. - return tableSaveMetaData(context, table, TSMD_ATLEAST_INDEXES); // guich@560_24 -} - -/** - * Drops a composed index. - * - * @param context The thread context where the function is being executed. - * @param table The table whose index is to dropped. - * @param columns The columns of the composed index. - * @param size The number of columns of the composed index. - * @param indexId The id of the composed index or -1 if its position is not known. - * @param saveMD Indicates if the meta data is to be saved. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the table does not have the desired composed index to be dropped. - */ -bool driverDropComposedIndex(Context context, Table* table, uint8* columns, int32 size, int32 indexId, bool saveMD) -{ - TRACE("driverDropComposedIndex") - ComposedIndex* compIndex = null; - ComposedIndex** compIndices = table->composedIndexes; - uint8* idxColumns; - int32 indexCount = size, - i = table->numberComposedIndexes, - j = 0; - - if (indexId < 0) - while (--i >= 0) - { - if ((compIndex = compIndices[i])->numberColumns == indexCount) - { - j = indexCount; - idxColumns = compIndex->columns; - while (--j >= 0 && columns[j] == idxColumns[j]); - - if (j < 0) - break; - } - } - else - compIndex = compIndices[i = indexId]; - - if (i >= 0 && compIndex) // Removes the index. - { - if (!indexRemove(context, compIndex->index)) - return false; - compIndex->index = null; - - // juliana@223_14: solved possible memory problems. - // juliana@201_16: When a composed index is deleted, its information is now deleted from the metadata. - if (table->numberComposedIndexes) - (table->composedIndexes[i] = table->composedIndexes[--table->numberComposedIndexes])->indexId = i + 1; - else - table->composedIndexes[table->numberComposedIndexes = 0] = null; - } - else // The given columns do not have a composed index. - { - // Builds the exception message. - char errorMsg[1024]; - CharP* columnNames = table->columnNames; - xstrcpy(errorMsg, columnNames[columns[0]]); - while (++j < indexCount) - { - xstrcpy(errorMsg, ", "); - xstrcpy(errorMsg, columnNames[columns[j]]); - } - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_DOESNOT_HAVE_AN_INDEX), errorMsg); - return false; - } - if (saveMD) - return tableSaveMetaData(context, table, TSMD_EVERYTHING); - return true; -} - -// juliana@227_6: drop index * on table_name wold make the index reapear after closing the driver and reusing table_name. -/** - * Deletes all indices of a table. - * - * @param context The thread context where the function is being executed. - * @param table The table whose indices are to dropped. - * @return The number of indices deleted. - */ -int32 deleteAllIndexes(Context context, Table* table) -{ - TRACE("deleteAllIndexes") - int32 count = 0, - i = table->columnCount, - primaryKey = table->primaryKeyCol; - bool ret = true; - Index** columnIndexes = table->columnIndexes; - uint8* columnAttrs = table->columnAttrs; - ComposedIndex** composedIndexes = table->composedIndexes; - - // Unique index. - while (--i >= 0) - if (i != primaryKey && columnIndexes[i]) - { - ret &= indexRemove(context, columnIndexes[i]); - columnIndexes[i] = null; - columnAttrs[i] &= ATTR_COLUMN_HAS_NO_INDEX; - count++; - } - - // juliana@201_33: When all indices are dropped by the user, the composed primary key can't be deleted. - i = table->numberComposedIndexes; - primaryKey = table->composedPK; - while (--i >= 0) - if (i != primaryKey) - { - ret &= driverDropComposedIndex(context, table, composedIndexes[i]->columns, composedIndexes[i]->numberColumns, i, false); - count++; - } - - // juliana@230_33: corrected a bug of composed indices files returning after deleting all indices. - if (!tableSaveMetaData(context, table, TSMD_EVERYTHING)) // guich@560_24 - return -1; - return ret? count : -1; -} - -/** - * Computes the column offsets of the table columns. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If there is not enougth memory to be allocated. - */ -bool computeColumnOffsets(Context context, Table* table) // rnovais@568_10: changed from static to global. -{ - TRACE("computeColumnOffsets") - uint16* offsets = table->columnOffsets; - int8* types = table->columnTypes; - bool notRecomputing = !offsets; - int32 sum = 0, - n = table->columnCount, - i = -1; - uint8* buffer; - - if (notRecomputing) // Does not create the array 2 times. - offsets = table->columnOffsets = (uint16*)TC_heapAlloc(table->heap, (n + 1) << 1); - - while (++i < n) - { - offsets[i] = sum; // Total offset till now. - sum += typeSizes[types[i]]; // Gets the size of this column. - } - offsets[i] = sum; // The offset for the last column. - - // the number of bytes necessary to store the columns. Each column in a table correspond to one bit. - // Added a number of bytes corresponding to the null values and to the crc code. - sum += NUMBEROFBYTES(n) + 4; // juliana@220_4 - - plainSetRowSize(&table->db, sum, buffer = TC_heapAlloc(table->heap, sum)); // Sets the new row size. - - if (notRecomputing) - { - Hashtable* htName2index; - int32* columnHashes = table->columnHashes; - table->htName2index = TC_htNew(n + 1, null); - htName2index = &table->htName2index; - if (!htName2index->items) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - if (!*table->name) - { - while (--n >= 0) - TC_htPut32(htName2index, columnHashes[n], n); - } - else - { - while (--n >= 0) - { - // juliana@223_14: solved possible memory problems. - if (TC_htGet32Inv(htName2index, columnHashes[n]) >= 0) - { - plainRemove(context, &table->db, table->sourcePath); - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DUPLICATED_COLUMN_NAME), (table->columnNames[n])); - return false; - } - TC_htPut32(htName2index, columnHashes[n], n); - } - } - } - return true; -} - -/** - * Loads the meta data of a table, - * - * @param context The thread context where the function is being executed. - * @param table The table being loaded. - * @param throwException Indicates that a TableNotClosedException should be thrown. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the table is in an incompatible format. - * @throws TableNotClosedException If the table was not properly close when opened last time. - * @throws OutOfMemoryError If there is not enougth memory to be allocated. - */ -bool tableLoadMetaData(Context context, Table* table, bool throwException) // juliana@220_5 -{ - TRACE("tableLoadMetaData") - TCHARP sourcePath = table->sourcePath; - CharP tableName = table->name; - int32 flags, - columnCount = 0, - i = -1, - numOfBytes, - version = 0, - nameLength, - indexNameLength, - stringLength; - bool exist; - PlainDB* plainDB = &table->db; - XFile* dbFile = &plainDB->db; - TCHAR indexName[MAX_PATHNAME]; - uint8 buffer[512]; // A buffer for small metadata. - IntBuf intBuf; - uint8* metadata = plainReadMetaData(context, plainDB, buffer); // Reads the meta data. - uint8* ptr = metadata; - uint8* columnAttrs; - int8* columnTypes; - int32* columnSizes; - CharP* columnNames; - int32* columnSizesIdx; - int32* columnHashes; - int8* columnTypesIdx; - SQLValue** defaultValues; - Heap heap = table->heap, - idxHeap = null; - NATIVE_FILE idxFile; - - if (!metadata) // juliana@223_14: solved possible memory problems. - { - nfClose(context, dbFile); - nfClose(context, &plainDB->dbo); - return false; - } - - // juliana@253_8: now Litebase supports weak cryptography. - if (!(((ptr[0] & USE_CRYPTO) == 0) ^ plainDB->db.useCrypto) && ptr[1] == ptr[2] == ptr[3] == 0 && (ptr[0] == 0 || ptr[0] == 1 || ptr[0] == 3)) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - nfClose(context, dbFile); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_WRONG_CRYPTO_FORMAT), 0); - goto error; - } - - if (ptr[0] == 1) - plainDB->useOldCrypto = true; - - plainDB->dbo.finalPos = plainDB->dbo.size; // Gets the last position of the blobs and strings file. - xmove2(&plainDB->headerSize, ptr + 4); // Reads the header size. - ptr += 6; - - // juliana@226_8: a table without metadata (with an empty .db, for instance) can't be recovered: it is corrupted. - if (!plainDB->headerSize) // The header size can't be zero. - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - // juliana@253_13: corrected a possible crash on Palm when trying to recover a table with corrupted header. - nfClose(context, dbFile); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), &table->name[5]); - return false; - } - - // If the header needs to be bigger, re-creates the metadata buffer with the correct size and skips the bytes already read. - if (plainDB->headerSize != DEFAULT_HEADER) - { - if (!(metadata = plainReadMetaData(context, plainDB, null))) - return false; - ptr = metadata + 6; - } - - // Checks if the table strings has the same format of the connection. - if ((((flags = *ptr++) & IS_ASCII) != 0) != plainDB->isAscii) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - nfClose(context, dbFile); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_WRONG_STRING_FORMAT)); - goto error; - } - - // juliana@220_2: added TableNotCreatedException which will be raised whenever a table is not closed properly. - // If the table was not correctly closed, throws an specific exception to the user. - if (!(flags &= IS_SAVED_CORRECTLY)) - { - if (throwException) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - nfClose(context, dbFile); - TC_throwExceptionNamed(context, "litebase.TableNotClosedException", getMessage(ERR_TABLE_NOT_CLOSED), &table->name[5]); - goto error; - } - else - plainDB->wasNotSavedCorrectly = true; - } - - // The tables version must be the same as Litebase version. - xmove2(&version, ptr); - - // juliana@230_12: improved recover table to take .dbo data into consideration. - if (version < VERSION_TABLE - 1) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - nfClose(context, dbFile); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_WRONG_VERSION), version); - goto error; - } - table->version = version; - - // The currentRowId is found from the last non-empty record, not from the metadata. - xmove4(&table->deletedRowsCount, ptr + 2); // Deleted rows count. - xmove4(&table->auxRowId, ptr + 6); // rnovais@570_61: reads the auxiliary rowid. - table->primaryKeyCol = *(ptr + 10); // juliana@114_9: the simple primary key column. - table->composedPK = *(ptr + 12); // The composed primary key index. - - // The column count can't be negative. - xmove2(&columnCount, ptr + 14); - ptr += 16; - if ((table->columnCount = columnCount) <= 0) - { - // juliana@222_1: the table should not be marked as closed properly if it was not previously closed correctly. - // juliana@253_13: corrected a possible crash on Palm when trying to recover a table with corrupted header. - nfClose(context, dbFile); - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_TABLE_CORRUPTED), &table->name[5]); - goto error; - } - - columnHashes = table->columnHashes = (int32*)TC_heapAlloc(heap, columnCount << 2); - columnTypes = table->columnTypes = (int8*)TC_heapAlloc(heap, columnCount); - columnSizes = table->columnSizes = (int32*)TC_heapAlloc(heap, columnCount << 2); - table->columnIndexes = (Index**)TC_heapAlloc(heap, columnCount * TSIZE); - columnAttrs = table->columnAttrs = (uint8*)TC_heapAlloc(heap, columnCount); - defaultValues = table->defaultValues = (SQLValue**)TC_heapAlloc(heap, columnCount * TSIZE); - table->storeNulls = (uint8*)TC_heapAlloc(heap, NUMBEROFBYTES(columnCount)); - - xmemmove(columnAttrs, ptr, columnCount); // Reads the column attributes. - - i = -1; - ptr += columnCount; - while (++i < columnCount) // Reads the column types. - columnTypes[i] = *ptr++; - - xmemmove(columnSizes, ptr, columnCount << 2); // Reads the column sizes. - - // Reads the column names. - ptr = readStringArray(ptr += (columnCount << 2), &table->columnNames, columnCount, heap); - columnNames = table->columnNames; - - i = -1; - while (++i < columnCount) // Computes the hashes. - columnHashes[i] = TC_hashCode(columnNames[i]); - - if (!computeColumnOffsets(context, table)) // Computes the column offsets. - goto error; - - // juliana@201_21: The null columns information must be created before openning the indices when reading the table meta data. - table->columnNulls = (uint8*)TC_heapAlloc(heap, numOfBytes = NUMBEROFBYTES(columnCount)); - - // juliana@224_5: corrected a bug that would throw an exception when re-creating an erased index file. - getFullFileName(tableName, sourcePath, indexName); - indexName[nameLength = tcslen(indexName)] = '$'; - - i = columnCount; - while (--i >= 0) // Loads the indices. - { - if ((columnAttrs[i] & ATTR_COLUMN_HAS_INDEX)) - { - idxHeap = heapCreate(); - IF_HEAP_ERROR(idxHeap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - heapDestroy(idxHeap); - table->columnIndexes[i] = null; // juliana@270_22: solved a possible crash when the table is corrupted. - goto error; - } - - // rnovais@110_2: verifies if the index file exists, otherwise makes the re-index. - columnSizesIdx = (int32*)TC_heapAlloc(idxHeap, 4); - columnTypesIdx = (int8*)TC_heapAlloc(idxHeap, 1); - TC_CharP2TCHARPBuf(TC_int2str(i, intBuf), &indexName[nameLength + 1]); - tcscat(indexName, TEXT(IDK_EXT)); - indexNameLength = tcslen(indexName); - - // juliana@224_5: corrected a bug that would throw an exception when re-creating an erased index file. - // juliana@202_9: Corrected a bug that would cause indices that have an .idr whose files were erased to be built incorrectly. - if ((exist = lbfileExists(indexName)) && !flags) - { - if ((exist = lbfileCreate(&idxFile, indexName, READ_WRITE)) - || (exist = lbfileSetSize(&idxFile, 0)) || (exist = lbfileClose(&idxFile))) - { - char buffer[1024]; - TC_TCHARP2CharPBuf(indexName, buffer); - fileError(context, exist, buffer); - heapDestroy(idxHeap); - table->columnIndexes[i] = null; // juliana@270_22: solved a possible crash when the table is corrupted. - goto error; - } - exist = false; - } - - *columnSizesIdx = columnSizes[i]; - *columnTypesIdx = columnTypes[i]; - - // juliana@230_8: corrected a possible index corruption if its files are deleted and the application crashes after recreating it. - if (!indexCreateIndex(context, table, tableName, i, columnSizesIdx, columnTypesIdx, exist, idxHeap) - || (!exist && flags && (!tableReIndex(context, table, i, false, null) || !setModified(context, table)))) - { - heapDestroy(idxHeap); - table->columnIndexes[i] = null; // juliana@270_22: solved a possible crash when the table is corrupted. - goto error; - } - } - } - - // juliana@213_6: current rowid was not being corrected fetched from the table. - // Now the current rowid can be fetched. - if (plainDB->rowCount) - { - if (plainRead(context, plainDB, plainDB->rowCount - 1)) - if (table->auxRowId != ATTR_DEFAULT_AUX_ROWID) //rnovais@570_61 - table->currentRowId = table->auxRowId; - else - { - xmove4(&table->currentRowId, plainDB->basbuf); - table->currentRowId = (table->currentRowId & ROW_ID_MASK) + 1; - } - else - goto error; - } - - i = -1; - while (++i < columnCount) // Reads the default values. - { - if ((columnAttrs[i] & ATTR_COLUMN_HAS_DEFAULT)) // Tests if it has default values. - { - defaultValues[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - switch (columnTypes[i]) - { - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - stringLength = 0; - xmove2(&stringLength, ptr); - - // juliana@252_5: corrected a bug when using a default value of type string which could become messed up. - defaultValues[i]->asChars = (JCharP)TC_heapAlloc(heap, stringLength << 1); - xmemmove(defaultValues[i]->asChars, (JCharP)(ptr + 2), stringLength << 1); - - // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - ptr += (((defaultValues[i]->length = stringLength) << 1) + 2); - - break; - - case SHORT_TYPE: - xmove2(&defaultValues[i]->asShort, ptr); - ptr += 2; // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - break; - - case DATE_TYPE: // Stored as int. - case INT_TYPE: - xmove4(&defaultValues[i]->asInt, ptr); - ptr += 4; // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - break; - - case LONG_TYPE: - xmove8(&defaultValues[i]->asLong, ptr); - ptr += 8; // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - break; - - case FLOAT_TYPE: - xmove4(&defaultValues[i]->asFloat, ptr); - ptr += 4; // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - break; - - case DOUBLE_TYPE: - READ_DOUBLE((uint8*)&defaultValues[i]->asDouble, ptr); - ptr += 8; // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - break; - - case DATETIME_TYPE: - xmove4(&defaultValues[i]->asDate, ptr); // date - xmove4(&defaultValues[i]->asTime, ptr + 4); // time - ptr += 8; // juliana@202_11: Corrected a bug that would create a composed index when opening a table using default values. - } - } - } - - // Reads the composed indices. - if ((table->numberComposedIndexes = *ptr++) > 0) // Reads the composed indices. - { - int32 j, - indexId, - numColumns, - size, - numberComposedIndexes = table->numberComposedIndexes; - uint8* columns; - - indexName[nameLength] = '&'; - i = -1; - while (++i < numberComposedIndexes) - { - idxHeap = heapCreate(); - IF_HEAP_ERROR(idxHeap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - heapDestroy(idxHeap); - table->composedIndexes[indexId] = null; // juliana@270_22: solved a possible crash when the table is corrupted. - goto error; - } - - indexId = (int8)*ptr++; // The composed index id. - numColumns = *ptr++; // Number of columns on the composed index. - size = numColumns << 2; - ptr++; - columns = (uint8*)TC_heapAlloc(idxHeap, numColumns); - columnSizesIdx = (int32*)TC_heapAlloc(idxHeap, size); - columnTypesIdx = (int8*)TC_heapAlloc(idxHeap, numColumns); - - j = -1; - while (++j < numColumns) - { - columns[j] = *ptr++; // Columns of this composed index. - columnSizesIdx[j] = columnSizes[columns[j]]; - columnTypesIdx[j] = columnTypes[columns[j]]; - } - - TC_CharP2TCHARPBuf(TC_int2str(i + 1, intBuf), &indexName[nameLength + 1]); - tcscat(indexName, TEXT(IDK_EXT)); - indexNameLength = tcslen(indexName); - - // juliana@224_5: corrected a bug that would throw an exception when re-creating an erased index file. - // juliana@202_9: Corrected a bug that would cause indices that have an .idr whose files were erased to be built incorrectly. - if ((exist = lbfileExists(indexName)) && !flags) - { - if ((exist = lbfileCreate(&idxFile, indexName, READ_WRITE)) - || (exist = lbfileSetSize(&idxFile, 0)) - || (exist = lbfileClose(&idxFile))) - { - char buffer[1024]; - TC_TCHARP2CharPBuf(indexName, buffer); - fileError(context, exist, buffer); - heapDestroy(idxHeap); - table->composedIndexes[indexId] = null; // juliana@270_22: solved a possible crash when the table is corrupted. - goto error; - } - exist = false; - } - - // juliana@230_8: corrected a possible index corruption if its files are deleted and the application crashes after recreating it. - // One of the files may not exist. - if (!indexCreateComposedIndex(context, table, table->name, columns, columnSizesIdx, columnTypesIdx, numColumns, indexId, false, exist, - idxHeap) - || (!exist && flags && (!tableReIndex(context, table, -1, false, table->composedIndexes[indexId - 1]) || !setModified(context, table)))) - { - heapDestroy(idxHeap); - table->composedIndexes[indexId] = null; // juliana@270_22: solved a possible crash when the table is corrupted. - goto error; - } - } - } - - // Reads the composed primary key. - if ((columnCount = table->numberComposedPKCols = *ptr++) > 0) // Number of the composed primary key. - xmemmove(table->composedPrimaryKeyCols = (uint8*)TC_heapAlloc(heap, columnCount), ptr, columnCount); - - if (plainDB->headerSize != DEFAULT_HEADER) - xfree(metadata); - return true; - -error: - if (plainDB->headerSize != DEFAULT_HEADER) - xfree(metadata); - return false; -} - -/** - * Saves the table meta data - * - * @param context The thread context where the function is being executed. - * @param table The table whose meta data is being saved. - * @param saveType The kind of save. It can be one out of TSMD_ONLY_DELETEDROWSCOUNT, - * TSMD_ONLY_PRIMARYKEYCOL, TSMD_EVERYTHING, or TSMD_ONLY_AUXROWID. - * @throws OutOfMemoryError If there is not enougth memory to be allocated. - */ -bool tableSaveMetaData(Context context, Table* table, int32 saveType) -{ - // Stores the changeable information. - TRACE("tableSaveMetaData") - uint8 buf[SECTOR_SIZE]; // Avoids allocating memory when saving small parts - guich@570_97: increased from 30 to 512. - uint32 n = table->columnCount, - i = -1, - size; - uint8* ptr0 = null; - uint8* ptr; - uint8* columnAttrs = table->columnAttrs; - int8* columnTypes = table->columnTypes; - int32* columnSizes = table->columnSizes; - SQLValue** defaultValues = table->defaultValues; - ComposedIndex* compIndex; - bool ret = true; - PlainDB* plainDB = &table->db; - - // Calculates the meta data size; - size = getTSMDSize(table, saveType); - if (saveType == TSMD_EVERYTHING) - size += getStringsTotalSize(table->columnNames, table->columnCount) + computeDefaultValuesMetadataSize(table) - + computeComposedIndicesTotalSize(table); - - // Tries to use a static buffer if possible. - if (size <= SECTOR_SIZE) - { - ptr0 = ptr = buf; - xmemzero(ptr, size); - } - else if (!(ptr0 = ptr = (uint8*)xmalloc(size))) // Must create a temporary buffer - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - // The strings and blobs final position is deprecated. - - *ptr = (plainDB->db.useCrypto? (plainDB->useOldCrypto? 1 : USE_CRYPTO) : 0); // juliana@253_8: now Litebase supports weak cryptography. - xmove2(ptr + 4, &plainDB->headerSize); // Saves the header size. - ptr += 6; - *ptr++ = plainDB->isAscii? IS_ASCII | !table->isModified : !table->isModified; // juliana@226_4: table is not saved correctly yet if modified. - - // The table format version. - i = table->version; // juliana@230_12 - xmove2(ptr, &i); - - xmove4(ptr + 2, &table->deletedRowsCount); // Saves the deleted rows count. - ptr += 6; - - if (saveType != TSMD_ONLY_DELETEDROWSCOUNT) // More things other than the deleted rows count must be saved. - { - xmove4(ptr, &table->auxRowId); // rnovais@570_61: saves the auxiliary rowid. - ptr += 4; - - if (saveType != TSMD_ONLY_AUXROWID) // More things other than the auxiliary row id must be saved. - { - // juliana@230_5: Corrected a AIOBE when using a table created on Windows 32, Windows CE, Linux, Palm, Android, iPhone, or iPad using - // primary key on BlackBerry and Eclipse. - *ptr++ = table->primaryKeyCol; // Saves the primary key col. - *ptr++ = 0; - *ptr++ = table->composedPK; // juliana@114_9: saves the composed primary key index. - *ptr++ = 0; - - if (saveType != TSMD_ONLY_PRIMARYKEYCOL) // More things other than the primary key col must be saved. - { - xmove2(ptr, &n); // Saves the number of columns. - ptr += 2; - i = -1; - xmemmove(ptr, columnAttrs, n); // Saves the column attributes. - ptr += n; - - if (saveType == TSMD_EVERYTHING) // Stores the rest. - { - i = -1; - while (++i < n) // Stores the column types. - *ptr++ = (uint8)columnTypes[i]; - xmemmove(ptr, columnSizes, n << 2); // Stores the column sizes. - ptr = writeStringArray(ptr += (n << 2), table->columnNames, table->columnCount); // Stores the column names. - - i = 0; - while (++i < n) // Saves the default values. - { - if ((columnAttrs[i] & ATTR_COLUMN_HAS_DEFAULT)) - switch (columnTypes[i]) - { - case CHARS_NOCASE_TYPE: - case CHARS_TYPE: - ptr = writeString16(ptr, defaultValues[i]->asChars, MIN((int32)defaultValues[i]->length, columnSizes[i])); - break; - - case SHORT_TYPE: - xmove2(ptr, &defaultValues[i]->asShort); - ptr += 2; - break; - - case DATE_TYPE: - case INT_TYPE: - xmove4(ptr, &defaultValues[i]->asInt); - ptr += 4; - break; - - case LONG_TYPE: - xmove8(ptr, &defaultValues[i]->asLong); - ptr += 8; - break; - - case FLOAT_TYPE: - xmove4(ptr, &defaultValues[i]->asFloat); - break; - - case DOUBLE_TYPE: - ptr = (uint8*)READ_DOUBLE(ptr, (uint8*)&defaultValues[i]->asDouble); - break; - - case DATETIME_TYPE: - xmove4(ptr, &defaultValues[i]->asDate); - xmove4(ptr + 4, &defaultValues[i]->asTime); - ptr += 8; - } - } - - n = *ptr++ = table->numberComposedIndexes; // Number of composed indices. - i = -1; - while (++i < n) // Stores the composed indices. - { - *ptr++ = (compIndex = table->composedIndexes[i])->indexId; // The composed index id. - *ptr = compIndex->numberColumns; // Number of columns on the composed index. - ptr += 2; - - // juliana@201_16 - xmemmove(ptr, compIndex->columns, compIndex->numberColumns); // Columns of this composed index. - ptr += compIndex->numberColumns; - } - n = *ptr++ = table->numberComposedPKCols; // Number of columns on composed primary key. If 0, there's no composed primary key. - xmemmove(ptr, table->composedPrimaryKeyCols, n); // Stores the composed primary key. - ptr += n; - } - } - } - } - - if (!plainWriteMetaData(context, plainDB, ptr0, (int32)(ptr - ptr0))) - ret = false; - else - flushCache(context, &plainDB->db); // juliana@223_11: table meta data is now always flushed imediately after being changed. - if (size > SECTOR_SIZE) - xfree(ptr0); - return ret; -} - -/** - * Sets the meta data for a table. - * - * @param context The thread context where the function is being executed. - * @param table The table being set. - * @param names The table column names. - * @param hashes The table column names hash codes. - * @param types The table column types. - * @param sizes The table column sizes. - * @param attrs The table column attributtes. - * @param composedPKCols The table primary key column. - * @param defaultValues The default values of the table columns. - * @param primaryKeyCol The table primary key column. - * @param composedPK The composed primary key index in the composed indices. - * @param columnCount The number of columns of the table. - * @param ComposedPKColsSize The number of composed primary keys. - * @return false if an error occurs; true, otherwise. - * @throws AlreadyCreatedException if the table is already created. - */ -bool tableSetMetaData(Context context, Table* table, CharP* names, int32* hashes, int8* types, int32* sizes, uint8* attrs, uint8* composedPKCols, - SQLValue** defaultValues, int32 primaryKeyCol, int32 composedPK, int32 columnCount, int32 composedPKColsSize) -{ - TRACE("tableSetMetaData") - Heap heap = table->heap; - int32 numOfBytes = NUMBEROFBYTES(columnCount); - - table->columnCount = (uint8)columnCount; // Sets the number of columns. - table->columnHashes = hashes; // Sets the column hashes. - table->columnTypes = types; // Sets the column types. - table->columnSizes = sizes; // Sets the column sizes. - table->storeNulls = TC_heapAlloc(heap, numOfBytes); // Initializes the arrays for the nulls. - table->columnNames = names; // Sets the column names. - - // The number of bytes necessary to store the nulls. Each column in a table correspond to one bit. - table->columnNulls = TC_heapAlloc(heap, numOfBytes); - - if (!computeColumnOffsets(context, table)) // guich@570_97: computes the column offests. - return false; - - if (*table->name) // juliana@201_14: It is not necessary to save the meta data in the .db for memory tables. - { - if (table->db.db.size) // The table can't be already created. - { - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_TABLE_ALREADY_CREATED), table->name); - return false; - } - - // Saves the meta data after everything was set. - table->version = VERSION_TABLE; // juliana@230_12 - table->columnAttrs = attrs; // Sets the column attributes. - table->defaultValues = defaultValues; // Sets the defaut values. - table->primaryKeyCol = (uint8)primaryKeyCol; // Primary key column. - table->composedPK = (uint8)composedPK; // Composed primary key index. - - // Sets the composed primary key info. - table->numberComposedPKCols = composedPKColsSize; - table->composedPrimaryKeyCols = composedPKCols; - - table->columnIndexes = (Index**)TC_heapAlloc(heap, columnCount * TSIZE); // Initializes the indices. - return tableSaveMetaData(context, table, TSMD_EVERYTHING); // Saves the metadata after everything was set. - } - return true; -} - -/** - * Gests the table standart metadata size to save a table. - * - * @param table The table to be saved. - * @param saveType The save type of the table, which increases or decreases its size. - * @return The metadata size. - */ -int32 getTSMDSize(Table* table, int32 saveType) -{ - TRACE("getTSMDSize") - int32 columnCount = table->columnCount, - size = 13; // dbo.finalPos + headerSize + flags + version + deletedRowsCount. - - if (saveType != TSMD_ONLY_DELETEDROWSCOUNT) - { - size += 4; // auxRowId. // rnovais@570_61 - if (saveType != TSMD_ONLY_AUXROWID) - { - size += 4; // primaryKeyCol + composedPK. - if (saveType != TSMD_ONLY_PRIMARYKEYCOL) - size += 2 + columnCount; // columnCount + columnAttributes. - } - } - if (saveType == TSMD_EVERYTHING) - size += columnCount + (columnCount << 2) + 2; // columnTypes + columnSizes + columnNames.length. - - return size; -} - -/** - * Gets the total size of the table column names. - * - * @param names The names of the columns. - * @param count The number of colums. - * @return the total size for the column names. - */ -int32 getStringsTotalSize(CharP* names, int32 count) -{ - TRACE("getStringsTotalSize") - int32 size = 0; - - while (--count >= 0) - size += xstrlen(*names++) + 2; // Includes the two bytes used for the length of each name. - return size; -} - -/** - * Gets the total size needed to store the table default values. - * - * @param table The table. - * @return the total size for the default values. - */ -int32 computeDefaultValuesMetadataSize(Table* table) -{ - TRACE("computeDefaultValuesMetadataSize") - uint32 i = table->columnCount, - size = 0; - uint8* columnAttrs = table->columnAttrs; - int8* columnTypes = table->columnTypes; - SQLValue** defaultValues = table->defaultValues; - - while (--i) - { - if ((columnAttrs[i] & ATTR_COLUMN_HAS_DEFAULT)) - switch (columnTypes[i]) - { - case CHARS_NOCASE_TYPE: - case CHARS_TYPE: - size += (defaultValues[i]->length << 1) + 2; // The stringh + its length. - break; - case SHORT_TYPE: - size += 2; - break; - case DATE_TYPE: - case INT_TYPE: - case FLOAT_TYPE: - size += 4; - break; - case LONG_TYPE: - case DOUBLE_TYPE: - case DATETIME_TYPE: - size += 8; - } - } - return size; -} - -/** - * Gets the total size needed to store the table composed indices. - * - * @param table The table. - * @return the total size for the composed indices. - */ -int32 computeComposedIndicesTotalSize(Table* table) -{ - TRACE("computeComposedIndicesTotalSize") - int32 size = 2 + table->numberComposedPKCols, // The number o composed PK + its number of columns. - i = table->numberComposedIndexes; - ComposedIndex** composedIndexes = table->composedIndexes; - - while (--i >= 0) - size += 3 + composedIndexes[i]->numberColumns; // id + numberColumns + hasIdr + the columns. - return size; -} - -/** - * Reorder the values of a statement to match the table definition. - * - * @param context The thread context where the function is being executed. - * @param table The table of the statement. - * @param fields The fields of the statement. - * @param record The record of values of thestatement. - * @param storeNulls Indicates which values have a null. - * @param nValues The number of values. - * @param paramIndexes The indices of the parameters, if any, in the record. - * @return false if there is an invalid field name; true, otherwise. - * @throws DriverException if there is an invalid field name. - */ -bool reorder(Context context, Table* table, CharP* fields, SQLValue** record, uint8* storeNulls, uint8* nValues, uint8* paramIndexes) -{ - TRACE("reorder") - int32 count = table->columnCount, - length = *nValues, - i = -1, - numParams = 0; - - // juliana@225_5: corrected a possible crash when the table has 128 columns. - SQLValue* outRecord[MAXIMUMS + 1]; // Just to store the temporary values, which will be copied over later. - - SQLValue* value; - uint8* tableStoreNulls = table->storeNulls; - Hashtable* htName2index = &table->htName2index; - JCharP asChars; - - if (!*fields) - *fields = "rowid"; // Inserts the rowid. - - // Cleans the storeNulls. - xmemzero(tableStoreNulls, NUMBEROFBYTES(count)); - xmemzero(outRecord, (MAXIMUMS + 1) * TSIZE); // juliana@225_5. - - // juliana@230_9: solved a bug of prepared statement wrong parameter dealing. - // Finds the index of the field on the table and reorders the record. - while (++i < length) // Makes sure that the fields are in table creation order. - { - int32 idx = TC_htGet32Inv(htName2index, TC_hashCode(fields[i])); - if (idx < 0) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_COLUMN_NAME), fields[i]); - return false; - } - - setBit(tableStoreNulls, idx, isBitSet(storeNulls, i)); - if ((value = outRecord[idx] = record[i]) && (asChars = value->asChars) && asChars[0] == (JChar)'?' && !asChars[1]) - paramIndexes[numParams++] = idx; - } - *nValues = count; - - xmemmove(record, outRecord, count * TSIZE); // Saves the ordered record. - return true; -} - -// juliana@220_3 -/** - * Sorts a table, using an ORDER BY or GROUP BY clause. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param groupByClause The group by clause. - * @param orderByClause The order by clause. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -bool sortTable(Context context, Table* table, SQLColumnListClause* groupByClause, SQLColumnListClause* orderByClause) -{ - TRACE("sortTable") - PlainDB* plainDB = &table->db; - int32 count = table->columnCount, - totalRecords = plainDB->rowCount; - SQLColumnListClause* sortListClause; - uint8* bufAux; - Heap heap = heapCreate(); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - heapDestroy(heap); - return false; - } - - bufAux = TC_heapAlloc(heap, plainDB->rowSize); // juliana@114_8 - - // Binds the sort lists to the temp table columns. - if ((orderByClause && !bindColumnsSQLColumnListClause(context, orderByClause, &table->htName2index, table->columnTypes, null, 0)) - || (groupByClause && !bindColumnsSQLColumnListClause(context, groupByClause, &table->htName2index, table->columnTypes, null, 0))) - { - heapDestroy(heap); - return false; - } - - // Picks one of the Column List clauses as the sort list. - // Quick sorts the table. - // guich@223_16: corrected a bug that could crash the application on Windows 32 when the query has an order by or group by due to a compiler - // error. - sortListClause = orderByClause? orderByClause : groupByClause; - return quickSort(context, table, newSQLValues(count, heap), newSQLValues(count, heap), newSQLValues(count, heap), sortListClause->fieldList, - bufAux, 0, totalRecords - 1, sortListClause->fieldsCount, heap); -} - -// juliana@250_1: corrected a possible crash when doing ordering operations. -// juliana@220_3 -// juliana@227_20: corrected order by or group by with strings being too slow. -/** - * Quick sort method used to sort the table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param pivot The pivot of this partition; - * @param someRecord1 An auxiliar record to avoid re-creating it. - * @param someRecord2 An auxiliar record to avoid re-creating it. - * @param fieldList The order of comparison of the fields. - * @param bufAux A buffer to store the records. - * @param first The first index of this partition. - * @param last The last index of this partition. - * @param fieldsCount The number of fields in the field list. - * @param heap A heap to allocate temporary sort structures. - * @return false if an error occurs; true, otherwise. - */ -bool quickSort(Context context, Table* table, SQLValue** pivot, SQLValue** someRecord1, SQLValue** someRecord2, SQLResultSetField** fieldList, - uint8* bufAux, int32 first, int32 last, int32 fieldsCount, Heap heap) -{ - TRACE("quickSort") - PlainDB* plainDB = &table->db; - SQLResultSetField* field; - int32* vector = table->nodes; - uint8* basbuf = plainDB->basbuf; - uint8* columnNulls1 = table->columnNulls; - uint8 columnNulls2[NUMBEROFBYTES(MAXIMUMS + 1)], - columnNulls3[NUMBEROFBYTES(MAXIMUMS + 1)]; - int32 low, - count = fieldsCount, - high = last - first + 1, - pivotIndex, // guich@212_3: now using random partition (improves worst case 2000x). - rowSize = plainDB->rowSize; - uint32 size = 2, - columnSize; - StringArray** stringArray = (StringArray**)TC_heapAlloc(heap, high * TSIZE); - StringArray* tempStringArray; - - while (--high >= 0) - stringArray[high] = (StringArray*)TC_heapAlloc(heap, sizeof(StringArray) * fieldsCount); - - // juliana@268_2: solved possible crash using order by when a string order by field does not appear in the select field. - while (--count >= 0) // Only loads columns used by the sorting process. - { - pivotIndex = (field = fieldList[count])->tableColIndex; - if ((columnSize = field->table->columnSizes[TC_htGet32(&field->table->htName2index, field->tableColHashCode)])) - { - pivot[pivotIndex]->asChars = (JCharP)TC_heapAlloc(heap, (columnSize << 1) + 2); - someRecord1[pivotIndex]->asChars = (JCharP)TC_heapAlloc(heap, (columnSize << 1) + 2); - someRecord2[pivotIndex]->asChars = (JCharP)TC_heapAlloc(heap, (columnSize << 1) + 2); - } - } - - vector[0] = first; - vector[1] = last; - while (size) // guich@212_3: removed recursion (storing in a IntVector). - { - high = vector[--size]; - low = vector[--size]; - - // juliana@213_3: last can't be equal to first. - if (!readRecord(context, table, pivot, pivotIndex = (last = high) == (first = low)? last : randBetween(first, last), columnNulls3, fieldList, - fieldsCount, true, heap, stringArray)) - goto error; - - // Partition - while (true) - { - while (high >= low) // Finds the partitions. - { - // Only loads columns used by the sorting process. - if (!readRecord(context, table, someRecord1, low, columnNulls1, fieldList, fieldsCount, true, heap, stringArray)) - goto error; - if (compareRecords(someRecord1, pivot, columnNulls1, columnNulls3, fieldsCount, fieldList) >= 0) - break; - low++; - } - - xmemmove(bufAux, basbuf, rowSize); // juliana@114_8 - - while (high >= low) - { - if (!readRecord(context, table, someRecord2, high, columnNulls2, fieldList, fieldsCount, true, heap, stringArray)) - goto error; - if (compareRecords(someRecord2, pivot, columnNulls2, columnNulls3, fieldsCount, fieldList) <= 0) - break; - high--; - } - - if (low <= high) // Swap the records. - { - // juliana@114_8: optimized the swap of the records. Now the buffer is written at once. - tempStringArray = stringArray[low]; - stringArray[low] = stringArray[high]; - stringArray[high] = tempStringArray; - plainRewrite(context, plainDB, low++); - xmemmove(basbuf, bufAux, rowSize); - plainRewrite(context, plainDB, high--); - } - else break; - } - - // Sorts the partitions. - if (first < high) - { - vector[size++] = first; - vector[size++] = high; - } - if (low < last) - { - vector[size++] = low; - vector[size++] = last; - } - } - - heapDestroy(heap); - return true; - -error: - heapDestroy(heap); - return false; -} - -/** - * Compares two records. Used for sorting the table to build the indices from scratch. - * - * @param recSize The size of the records being compared. - * @param vals1 The first record of the comparison. - * @param vals2 The second record of the comparison. - * @param types The types of the record values. - * @return A positive number if vals1 > vals2; 0 if vals1 == vals2; -1, otherwise. It will return MAX_INT_VALUE if both records are - * equal but the record of the first is greater than the second, and MIN_INT_VALUE if both records are equal but the record of the - * first is less than the second. - */ -int32 compareSortRecords(int32 recSize, SQLValue** vals1, SQLValue** vals2, int8* types) // juliana@201_3 -{ - TRACE("compareSortRecords") - int32 i = -1, - result; - - while (++i < recSize) // Does the comparison between the values till one of them is different from zero. - if ((result = valueCompareTo(null, vals1[i], vals2[i], types[i], false, false, null)) != 0) - return result; - - // The values are equal. Compares with the record index. - if ((result = types[0] == DATETIME_TYPE) || result == LONG_TYPE || result == DOUBLE_TYPE) - { - if (vals1[0]->length > vals2[0]->length) - return MAX_SHORT_VALUE; - if (vals1[0]->length < vals2[0]->length) - return MIN_SHORT_VALUE; - return 0; - } - if (vals1[0]->asTime > vals2[0]->asTime) - return MAX_SHORT_VALUE; - if (vals1[0]->asTime < vals2[0]->asTime) - return MIN_SHORT_VALUE; - return 0; -} - -// juliana@250_1: corrected a possible crash when doing ordering operations. -/** - * Quick sort used for sorting the table to build the indices from scratch. This one is simpler than the sort used for order / gropu by. - * Uses a stack instead of a recursion. - * - * @param sortValues The records to be sorted. - * @param recSize The size of the records being sorted. - * @param types The types of the record values. - * @param first The first element of current partition. - * @param last The last element of the current. - * @param vector A temporary array to use in the recursion. - */ -void sortRecords(SQLValue*** sortValues, int32 recSize, int8* types, int32 first, int32 last, int32* vector) // juliana@201_3 -{ - TRACE("sortRecords") - SQLValue** mid; - SQLValue** tempValues; - int32 low, - high, - i; - uint32 size = 2; - - // guich@212_3: checks if the values are already in order. - i = first; - while (++i <= last) - if (compareSortRecords(recSize, sortValues[i - 1], sortValues[i], types) > 0) - break; - if (i > last) - return; - - // Not fully sorted? - vector[0] = first; - vector[1] = last; - while (size) // guich@212_3: removed recursion (storing in a stack). - { - high = vector[--size]; - low = vector[--size]; - - // juliana@213_3: last can't be equal to first. - mid = sortValues[(last = high) == (first = low)? last : randBetween(first, last)]; - - while (true) // Finds the partitions. - { - while (high >= low && compareSortRecords(recSize, mid, sortValues[low], types) > 0) - low++; - while (high >= low && compareSortRecords(recSize, mid, sortValues[high], types) < 0) - high--; - - if (low <= high) - { - tempValues = sortValues[low]; - sortValues[low++] = sortValues[high]; - sortValues[high--] = tempValues; - } - else break; - } - - // Sorts the partitions. - if (first < high) - { - vector[size++] = first; - vector[size++] = high; - } - if (low < last) - { - vector[size++] = low; - vector[size++] = last; - } - } -} - -/** - * Does a radix sort on the given SQLValue array. Only integral types are allowed (SHORT, INT, LONG). This is faster than quicksort. Also used to - * build the indices from scratch. - * - * @param source The values to be sorted. Only simple records for simple indices can be used. - * @param length The number of values to be sorted. - * @param type The type of the elements. - * @param temp A temporary array for the sort. - */ -void radixSort(SQLValue*** source, int32 length, int32 type, SQLValue*** temp) // juliana@201_3 -{ - TRACE("radixSort") - int32 count[256]; - int32 index[256]; - int32 byteCount = (type == INT_TYPE)? 4 : (type == SHORT_TYPE)? 2 : 8, - i = 0; - int64 mask = 0xFF, - bits; - SQLValue*** aux; - - xmemzero(count, 1024); - xmemzero(index, 1024); - - bits = radixPass(0, source, temp, count, index, type, length); - i = 0; - while (++i < byteCount) - { - mask <<= 8; - if ((bits & mask) != 0) // Any bits in this range? - { - // Swaps the from/to arrays. - aux = source; - source = temp; - temp = aux; - - radixPass(i, source, temp, count, index, type, length); // Yes, sort. - } - } - if (temp != source) // If the final sorted array is not at the source, copies to it. - xmemmove(source, temp, length * TSIZE); -} - -/** - * Executes a pass of the radix sort. - * - * @param start Start bit. - * @param source The source array, - * @param dest The dest array where the operations with the source are copied to. - * @param count A temporary array. - * @param index A temporary array. - * @param type The type of the values being sorted. - * @param length The number of values to be sorted. - * @return A number of bits. - */ -int64 radixPass(int32 start, SQLValue*** source, SQLValue*** dest, int32* count, int32* index, int32 type, int32 length) // juliana@201_3 -{ - TRACE("radixPass") - int32 i = 0, - n = length, - ibits = 0, - b, - ishift = start << 3; - int64 lbits = 0, - lshift = (int64)start << 3, - lb; - - if (start > 0) - xmemzero(count, 1024); - - switch (type) - { - case INT_TYPE: - case DATE_TYPE: // juliana@214_9: index creation for date types could create corrupted indices. - if (start == 0) - while (--n >= 0) - { - count[(b = (*source[i++])->asInt) & 0xFF]++; - ibits |= b; - } - else if (start == 3) - while (--n >= 0) - count[((*source[i++])->asInt >> ishift) + 128]++; - else - while (--n >= 0) - count[((*source[i++])->asInt >> ishift) & 0xFF]++; - break; - - case SHORT_TYPE: - if (start == 0) - while (--n >= 0) - { - count[(b = (*source[i++])->asShort) & 0xFF]++; - ibits |= b; - } - else - while (--n >= 0) - count[((*source[i++])->asShort >> ishift) + 128]++; - break; - case LONG_TYPE: - if (start == 0) - while (--n >= 0) - { - count[(int32)((lb = (*source[i++])->asLong) & 0xFF)]++; - lbits |= lb; - } - else if (start == 7) - while (--n >= 0) - count[(int32)(((*source[i++])->asLong >> lshift) + 128)]++; - else - while (--n >= 0) - count[(int32)(((*source[i++])->asLong >> lshift) & 0xFF)]++; - } - - index[0] = i = 0; - n = 255; - while (--n >= 0) - { - index[i + 1] = index[i] + count[i]; - i++; - } - - i = 0; - n = length; - switch (type) - { - case INT_TYPE: - case DATE_TYPE: // juliana@214_9: index creation for date types could create corrupted indices. - if (start == 0) - while (--n >= 0) - { - dest[index[((*source[i])->asInt) & 0xFF]++] = source[i]; - i++; - } - else if (start == 3) - while (--n >= 0) - { - dest[index[((*source[i])->asInt >> ishift) + 128]++] = source[i]; - i++; - } - else - while (--n >= 0) - { - dest[index[((*source[i])->asInt >> ishift) & 0xFF]++] = source[i]; - i++; - } - break; - case SHORT_TYPE: - if (start == 0) - while (--n >= 0) - { - dest[index[((*source[i])->asShort) & 0xFF]++] = source[i]; - i++; - } - else - while (--n >= 0) - { - dest[index[((*source[i])->asShort >> ishift) + 128]++] = source[i]; - i++; - } - break; - case LONG_TYPE: - if (start == 0) - while (--n >= 0) - { - dest[index[(int32)(((*source[i])->asLong) & 0xFF)]++] = source[i]; - i++; - } - else if (start == 7) - while (--n >= 0) - { - dest[index[(int32)(((*source[i])->asLong >> lshift) + 128)]++] = source[i]; - i++; - } - else - while (--n >= 0) - { - dest[index[(int32)(((*source[i])->asLong >> lshift) & 0xFF)]++] = source[i]; - i++; - } - } - - return type == LONG_TYPE? lbits : ibits; -} - -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Creates the table files and loads its meta data if it was already created. - * - * @param context The thread context where the function is being executed. - * @param name The name of the table. - * @param sourcePath The path of the table on disk. - * @param create Indicates if the table is to be created or just opened. - * @param isAscii Indicates if the table strings are to be stored in the ascii format or in the unicode format. - * @param useCrypto Indicates if the table uses cryptography. - * @param nodes An array of nodes indices. - * @param throwException Indicates that a TableNotClosedException should be thrown. - * @param heap The table heap. - * @return The table created or null if an error occurs. - */ -Table* tableCreate(Context context, CharP name, TCHARP sourcePath, bool create, bool isAscii, bool useCrypto, int32* nodes, - bool throwException, Heap heap) // juliana@220_5 -{ - TRACE("tableCreate") - Table* table = (Table*)TC_heapAlloc(heap, sizeof(Table)); - PlainDB* plainDB = &table->db; - - table->currentRowId = 1; - table->auxRowId = ATTR_DEFAULT_AUX_ROWID; // rnovais@570_61 - table->sourcePath = sourcePath; - table->heap = heap; - table->nodes = nodes; - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - if (!createPlainDB(context, &table->db, name, create, useCrypto, sourcePath)) // Creates or opens the table files. - goto error; - - if (name && (plainDB->db.size || create)) // The table is already created if the .db is not empty. - { - xstrcpy(table->name, name); - plainDB->isAscii = isAscii; - if (plainDB->db.size && !tableLoadMetaData(context, table, throwException)) // juliana@220_5 - goto error; // juliana@220_8: does not let the table be truncated if an error occurs when loading its metadata. - } - return table; - -error: - freeTable(context, table, false, false); - return null; -} - -// juliana@114_9 -/** - * Creates a table, which can be stored on disk or on memory (result set table). - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param tableName The table name. - * @param names The table column names. - * @param hashes The table column hashes. - * @param types The table column types. - * @param sizes The table column sizes. - * @param attrs The table cxlumn attributes. - * @param defaultValues The table column default values. - * @param primaryKeyCol The primary key column. - * @param composedPK The composed primary key index in the composed indices. - * @param composedPKCols The columnns that are part of the composed primary key. - * @param ComposedPKColsSize The size of the composed primary key. - * @param count The column count. - * @param heap A heap to allocate the table. - * @return The table handle or null if an error occurs. - * @throws AlreadyCreatedException If the table already exists. - * @throws OutOfMemoryError If an memory allocation fails. - */ -Table* driverCreateTable(Context context, TCObject driver, CharP tableName, CharP* names, int32* hashes, int8* types, int32* sizes, uint8* attrs, - SQLValue** defaultValues, int32 primaryKeyCol, int32 composedPK, uint8* composedPKCols, int32 composedPKColsSize, int32 count, Heap heap) -{ - TRACE("driverCreateTable") - Table* table = null; - TCHARP sourcePath = getLitebaseSourcePath(driver); - int32 appCrid = OBJ_LitebaseAppCrid(driver); - Hashtable* htTables = getLitebaseHtTables(driver); - - if (!tableName) // Temporary table. - { - // rnovais@570_75 juliana@220_5 - if (!(table = tableCreate(context, null, sourcePath, true, false, false, getLitebaseNodes(driver), true, heap))) - return null; - - table->db.headerSize = 0; - - IF_HEAP_ERROR(heap) - { - goto error; - } - - table->isModified = true; // juliana@226_4 - table->answerCount = -1; // juliana@230_14 - if (!tableSetMetaData(context, table, null, hashes, types, sizes, null, null, null, -1, -1, count, 0)) - goto error; - } - else // Normal table. - { - char name[DBNAME_SIZE]; - - // guich@_105: First, checks if the table was already created in this connection. - if ((table = (Table*)TC_htGetPtr(htTables, TC_hashCode(tableName)))) - { - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_TABLE_ALREADY_CREATED), table->name); - return null; - } - - if (!getDiskTableName(context, appCrid, tableName, name)) // Gets the table real name. - return null; - - // juliana@220_5 - // juliana@253_8: now Litebase supports weak cryptography. - if (!(table = tableCreate(context, name, sourcePath, true, OBJ_LitebaseIsAscii(driver), OBJ_LitebaseUseCrypto(driver), - getLitebaseNodes(driver), true, heap))) - goto error; - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - // Doesn't put in the hashtable the temporary tables. - // Stores the meta data. - // Creates the index for the primary key or the composed index for the composed primary key. // juliana@114_9 - if (!TC_htPutPtr(htTables, TC_hashCode(tableName), table) - || !tableSetMetaData(context, table, names, hashes, types, sizes, attrs, composedPKCols, defaultValues, primaryKeyCol, composedPK, count, - composedPKColsSize) - || (primaryKeyCol != NO_PRIMARY_KEY && !driverCreateIndex(context, table, &hashes[primaryKeyCol], 0, 1, null)) - || (composedPKColsSize > 0 && !driverCreateIndex(context, table, null, 0, composedPKColsSize, composedPKCols))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - } - return table; - -error: - freeTable(context, table, true, false); - TC_htRemove(htTables, TC_hashCode(tableName)); - return null; -} - -/** - * Renames a table. This never happens to be a temporary ResultSet memory table. - * - * @param context The thread context where the function is being executed. - * @param driver The LitebaseConnection. - * @param table The table being renamed. - * @param newTableName The new table name. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool renameTable(Context context, TCObject driver, Table* table, CharP newTableName) // rnovais@566_10 -{ - TRACE("renameTable") - char result[DBNAME_SIZE]; - IntBuf intBuf; - char oldTableName[DBNAME_SIZE]; - TCHARP sourcePath = table->sourcePath; - int32 i, - length; - Hashtable* htTables = getLitebaseHtTables(driver); - Index** columnIndexes = table->columnIndexes; - ComposedIndex** composedIndexes = table->composedIndexes; - - // Gets the real name. - xstrcpy(oldTableName, &table->name[5]); - xmemmove(result, table->name, 5); - xstrcpy(&result[5], newTableName); - length = xstrlen(result); - - // Renames the table. - if (!plainRename(context, &table->db, result, sourcePath)) - return false; - xstrcpy(table->name, result); - - // Renames the indices. - i = table->columnCount; - result[length] = '$'; - while (--i >= 0) - { - if (columnIndexes[i]) - { - xstrcpy(&result[length + 1], TC_int2str(i, intBuf)); - if (!indexRename(context, columnIndexes[i], result)) // Renames the index files. - return false; - } - } - - // juliana@220_17: rename table now renames the composed indices. - i = table->numberComposedIndexes; - result[length] = '&'; - while (--i >= 0) - { - xstrcpy(&result[length + 1], TC_int2str(i + 1, intBuf)); - if (!indexRename(context, composedIndexes[i]->index, result)) // Renames the index files. - return false; - } - - if (!TC_htPutPtr(htTables, TC_hashCode(newTableName), table)) // Adds the new table name to the hash table. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - TC_htRemove(htTables, TC_hashCode(oldTableName)); // Removes the old table name from the hash table. - return true; -} - -/** - * Renames a column of a table. - * - * @param context The thread context where the function is being executed. - * @param table The table whose column is being renamed. - * @param oldColumn The name of the old column. - * @param newColumn The name of the new column. - * @param reuseSpace Indicates if the column name space can be reused. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the old column does not exist or the new column already exists. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool renameTableColumn(Context context, Table* table, CharP oldColumn, CharP newColumn, bool reuseSpace) // rnovais@566_17 -{ - TRACE("renameColumnTable") - Hashtable* htName2index = &table->htName2index; - int32 oldHash = TC_hashCode(oldColumn), - newHash = TC_hashCode(newColumn), - oldIdx = TC_htGet32Inv(htName2index, oldHash), // Gets the old column name. It must exist. - newIdx = TC_htGet32Inv(htName2index, newHash); // The new column name can't exist. - - if (oldIdx < 0) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_NOT_FOUND), oldColumn); - return false; - } - if (newIdx >= 0) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_COLUMN_ALREADY_EXIST), newColumn); - return false; - } - - // Changes the column information. - if (!TC_htPut32(&table->htName2index, newHash, oldIdx)) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - TC_htRemove(&table->htName2index, oldHash); - table->columnHashes[oldIdx] = oldHash; - if (reuseSpace) - xstrcpy(table->columnNames[oldIdx], newColumn); - else - table->columnNames[oldIdx] = newColumn; - return tableSaveMetaData(context, table, TSMD_EVERYTHING); -} - -// juliana@201_3: if an index is created after populating or purging the table, its nodes will be full in order to improve its usage and search speed. -/** - * Re-builds an index of a table. - * - * @param context The thread context where the function is being executed. - * @param table The table being re-indexed. - * @param column The table column number of the index or -1 for a composed index. - * @param isPKCreation Indicates that the index is of a primary key. - * @param composedIndex The composed index to be rebuilt or null in case of a simple index. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If there is a null in the primary key or a duplicated key. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -bool tableReIndex(Context context, Table* table, int32 column, bool isPKCreation, ComposedIndex* composedIndex) -{ - TRACE("tableReIndex") - Heap heap; - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - Index* index = (column != -1)? table->columnIndexes[column] : composedIndex->index; // Gets the index. - int32 n = plainDB->rowCount, - i = -1; - bool isDelayed = index->isWriteDelayed; - - if (!indexDeleteAllRows(context, index)) // Cleans the index values. - return false; - if (!indexSetWriteDelayed(context, index, true)) // This makes the index creation faster. - goto error1; - - if (index->isOrdered && !composedIndex) // Simple index using rowid. - { - SQLValue emptyValue; - SQLValue* value = &emptyValue; - xmemzero(&emptyValue, sizeof(SQLValue)); - - while (++i < n) - { - if (!plainRead(context, plainDB, i)) // Reads the row. - goto error1; - if (!recordNotDeleted(basbuf)) // Only gets non-deleted records. - continue; - - // juliana@230_12 - if (!readValue(context, plainDB, &emptyValue, 0, INT_TYPE, basbuf, false, false, false, -1, null) // juliana@220_3 - || !indexAddKey(context, index, &value, i)) - goto error1; - } - } - else - { - int32 k = 0, - rows = plainDB->rowCount, // juliana@284_1: Solved a possible application crash when recreating indices. - j = rows, - columnCount = table->columnCount, - indexCount = index->numberColumns, - bytes = NUMBEROFBYTES(columnCount), - indexSize = index->numberColumns, - type, - offset = 0, - compare; - bool isNull; - SQLValue*** values; - uint8* columnNulls0 = table->columnNulls; - uint8* nullsPosition = basbuf + table->columnOffsets[columnCount]; - uint8* columns = null; - uint16* columnOffsets = table->columnOffsets; - int8* types = index->types; - - if (!rows) // juliana@223_14: solved possible memory problems. - return indexSetWriteDelayed(context, index, isDelayed); - - // juliana@223_14: solved possible memory problems. - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error2; - } - - IF_HEAP_ERROR(table->heap) // juliana@223_14: solved possible memory problems. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error2; - } - - // juliana@270_25: solved a possible crash when an OutOfMemoryError occurs when creating or recreating indices. - IF_HEAP_ERROR(index->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error2; - } - - type = *types; - if (column != -1) - offset = columnOffsets[column]; - else - columns = composedIndex->columns; - - // Allocates the records for the sorting. - values = (SQLValue***)TC_heapAlloc(heap, TSIZE * rows); - while (--j >= 0) - values[j] = (SQLValue**)TC_heapAlloc(heap, TSIZE * indexCount); - - while (++i < n) - { - isNull = false; // Resets the null info. - - if (!plainRead(context, plainDB, i)) // Reads the row. - goto error2; - if (!recordNotDeleted(basbuf)) // Only gets non-deleted records. - continue; - - // juliana@201_22: the null columns information wasn't being read when re-creating an index. - xmemmove(columnNulls0, nullsPosition, bytes); - - j = indexSize; - while (--j >= 0) - values[k][j] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - - if (column != -1) - { - // juliana@230_12 - if (!readValue(context, plainDB, *values[k], offset, type, basbuf, false, isNull = isBitSet(columnNulls0, column), false, -1, heap)) - goto error2; - - // juliana@202_12: Corrected null values dealing when building an index. - // juliana@202_10: Corrected a bug that would cause a DriverException if there was a null in an index field when creating it after the table is populated. - if (isPKCreation && columnNulls0 && isNull) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_PK_CANT_BE_NULL), 0); - goto error2; - } - } - else - { - j = indexSize; - while (--j >= 0) - { - // juliana@230_12 - if (!readValue(context, plainDB, values[k][j], columnOffsets[columns[j]], types[j], basbuf, false, isNull |= isBitSet(columnNulls0, columns[j]), false, -1, heap)) - goto error2; - - // juliana@202_12: Corrected null values dealing when building an index. - // juliana@202_10: Corrected a bug that would cause a DriverException if there was a null in an index field when creating it after the table is populated. - if (isPKCreation && columnNulls0 && isNull) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_PK_CANT_BE_NULL)); - goto error2; - } - } - } - - if (isNull) // Do not store null records. - rows--; - else - // juliana@202_7: Corrected a bug that would cause long and double indices to be built incorrectly. - // The record value is stored in an empty field of the first record column value. - if (type == DATETIME_TYPE || type == LONG_TYPE || type == DOUBLE_TYPE) - (*values[k++])->length = i; - else - (*values[k++])->asTime = i; - } - - rows = k; // juliana@270_22: solved a possible crash when the table is corrupted on Android and possibly on other platforms. - if (!index->isOrdered) - { - // A radix sort is done for integer types. It is much more efficient than quick sort. - if (indexSize == 1 && (type == SHORT_TYPE || type == INT_TYPE || type == LONG_TYPE || type == DATE_TYPE)) - { - SQLValue*** tempValues = (SQLValue***)TC_heapAlloc(heap, rows * TSIZE); - radixSort(values, rows, type, tempValues); - } - else - sortRecords(values, indexSize, types, 0, rows - 1, table->nodes); - index->isOrdered = true; // The index elements will be inserted in the right order. - } - - k = -1; - while (++k < rows) - { - // If it is primary key, check first if there is violation. - if (isPKCreation && k > 0 && (!(compare = compareSortRecords(indexSize, values[k], values[k - 1], types)) || compare == MIN_SHORT_VALUE - || compare == MAX_SHORT_VALUE)) - { - TC_throwExceptionNamed(context, "litebase.PrimaryKeyViolationException", getMessage(ERR_STATEMENT_CREATE_DUPLICATED_PK), table->name); - - goto error2; // juliana@223_14: solved possible memory problems. - } - - // juliana@202_7: Corrected a bug that would cause long and double indices to be built incorrectly. - if (((type == DATETIME_TYPE || type == LONG_TYPE || type == DOUBLE_TYPE) && !indexAddKey(context, index, values[k], (*values[k])->length)) - || (type != DATETIME_TYPE && type != LONG_TYPE && type != DOUBLE_TYPE && !indexAddKey(context, index, values[k], (*values[k])->asTime))) - goto error2; // juliana@223_14: solved possible memory problems. - } - - if (!composedIndex || column) // An index beggining with rowid is always ordered. - index->isOrdered = false; - heapDestroy(heap); - } - - return indexSetWriteDelayed(context, index, isDelayed); // Uses the user desired delayed settings again. - -error2: - heapDestroy(heap); - if (column) // An index beggining with rowid is always ordered. - index->isOrdered = false; -error1: - indexSetWriteDelayed(context, index, isDelayed); - return false; -} - -/** - * Creates a simple index for the table for the given column. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param fullTableName The table disk name. - * @param columnIndex The column of the index. - * @param columnSizes The sizes of the columns. - * @param columnTypes The types of the columns. - * @param exist Indicates that the index files already exist. - * @param heap A heap to allocate the index structure. - * @return false if an error occurs; true, otherwise. - */ -bool indexCreateIndex(Context context, Table* table, CharP fullTableName, int32 columnIndex, int32* columnSizes, int8* columnTypes, bool exist, Heap heap) -{ - TRACE("indexCreateIndex") - char indexName[DBNAME_SIZE]; - IntBuf intBuf; - int32 tableLen = xstrlen(fullTableName); - - // The index name. - xstrcpy(indexName, fullTableName); - indexName[tableLen] = '$'; - xstrcpy(&indexName[tableLen + 1], TC_int2str(columnIndex, intBuf)); - - // rnovais@113_1 - if (!(table->columnIndexes[columnIndex] = createIndex(context, table, columnTypes, columnSizes, indexName, 1, exist, heap))) - return false; - - // rowid is always an ordered index. - table->columnIndexes[columnIndex]->isOrdered = !columnIndex; // guich@110_5 - table->columnAttrs[columnIndex] |= ATTR_COLUMN_HAS_INDEX; - return true; -} - -/** - * Creates a composed index for a given table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param fullTableName The table disk name. - * @param columnIndexes he columns of the index. - * @param columnSizes The sizes of the columns. - * @param columnTypes The types of the columns. - * @param numberColumns The number of columns of the index. - * @param newIndexNumber An id for the composed index. - * @param increaseArray Indicates if the composed indices array must be increased. - * @param exist Indicates that the index files already exist. - * @param heap A heap to allocate the index structure. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the maximum number of composed indices was achieved. - */ -bool indexCreateComposedIndex(Context context, Table* table, CharP fullTableName, uint8* columnIndexes, int32* columnSizes, int8* columnTypes, - int32 numberColumns, int32 newIndexNumber, bool increaseArray, bool exist, Heap heap) -{ - TRACE("indexCreateComposedIndex") - char indexName[DBNAME_SIZE]; - IntBuf intBuf; - int32 tableLen = xstrlen(fullTableName), - size = table->numberComposedIndexes, - id = (newIndexNumber < 0)? -newIndexNumber : newIndexNumber; // newIndexNumber < 0 means that this was a index. - ComposedIndex* composedIndex; - - // The index name. - xstrcpy(indexName, fullTableName); - indexName[tableLen] = '&'; - xstrcpy(&indexName[tableLen + 1], TC_int2str(id, intBuf)); // Passes the newIndex index id. - - composedIndex = createComposedIndex(id, columnIndexes, numberColumns, table->heap); - if (increaseArray && table->numberComposedIndexes == MAX_NUM_INDEXES_APPLIED) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_COMP_INDICES)); - return false; - } - - // Creates the index of the composed index. - if (!(composedIndex->index = createIndex(context, table, columnTypes, columnSizes, indexName, numberColumns, exist, heap))) - return false; - - if (increaseArray) - { - table->composedIndexes[size] = composedIndex; // New composed index. - table->numberComposedIndexes++; - } - else - table->composedIndexes[id - 1] = composedIndex; - - composedIndex->index->isOrdered = !*columnIndexes; // The rowid is the column 0. - return true; -} - -// juliana@220_3 -/** - * Reads the entire record from a table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param record An array where the record filed values will be stored. - * @param recPos The record index. - * @param columnNulls A buffer where the nulls will be stored. - * @param fieldList A field list that indicates which fields to read from the table. - * @param fieldsCount The number of fields in the field list. - * @param isTempBlob Indicates if a blob must be loaded or not. - * @param heap A heap to allocate the temporary strings when sorting a temporary table. - * @param stringArray A temporary string array used when sorting a temporary table. - * @return false if an error occurs; true, otherwise. - */ -bool readRecord(Context context, Table* table, SQLValue** record, int32 recPos, uint8* columnNulls, SQLResultSetField** fieldList, - int32 fieldsCount, bool isTempBlob, Heap heap, StringArray** stringArray) -{ - TRACE("readRecord") - int32 i, - j, - asLength; - bool isTemp = !*table->name; - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - uint16* columnOffsets = table->columnOffsets; - int8* columnTypes = table->columnTypes; - int32* columnSizes = table->columnSizes; // juliana@230_12 - JCharP asString; - - if (!plainRead(context, plainDB, recPos)) - return false; - - // juliana@226_12: corrected a bug that could make aggregation function not work properly. - if (!isTemp) - { - xmove4(&i, plainDB->basbuf); - if ((i & ROW_ATTR_MASK) == ROW_ATTR_DELETED) - return true; - } - - i = fieldList? fieldsCount : table->columnCount; - xmemmove(columnNulls, basbuf + columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - if (fieldList) // Reads only the columns used during sorting. - while (--i >= 0) - { - // juliana@227_20: corrected order by or group by with strings being too slow. - j = fieldList[i]->tableColIndex; - if ((columnTypes[j] != CHARS_TYPE && columnTypes[j] != CHARS_NOCASE_TYPE) || !stringArray[recPos][i].string) - { - // juliana@230_12 - if (!readValue(context, plainDB, record[j], columnOffsets[j], columnTypes[j], basbuf, isTemp, isBitSet(columnNulls, j), isTempBlob, -1, - null)) - return false; - if (record[j]->length) - { - asString = stringArray[recPos][i].string = (JCharP)TC_heapAlloc(heap, - (asLength = ((stringArray[recPos][i].length = record[j]->length) + 1) << 1) + 2); - xmemmove(asString, record[j]->asChars, asLength); - } - } - else - { - record[j]->length = stringArray[recPos][i].length; - xmemmove(record[j]->asChars, stringArray[recPos][i].string, record[j]->length << 1); - } - - } - - // juliana@230_12: improved recover table to take .dbo data into consideration. - else // Reads all columns of the table. - while (--i >= 0) - if (!readValue(context, plainDB, record[i], columnOffsets[i], columnTypes[i], basbuf, isTemp, isBitSet(columnNulls, i), isTempBlob, columnSizes[i], heap)) - return false; - return true; -} - -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Writes a record on a disk table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param values The values to be written on the table. - * @param recPos The record position. - * @param heap A heap to allocate temporary structures. - * @return false if an error occurs; true, otherwise. - */ -bool writeRecord(Context context, Table* table, SQLValue** values, int32 recPos, Heap heap) -{ - TRACE("writeRecord") - PlainDB* plainDB = &table->db; - XFile* db = &plainDB->db; - XFile* dbo = &plainDB->dbo; - int32 columnCount = table->columnCount, - i = columnCount, - writePos = -1, - rowid = 0, - primaryKeyCol = table->primaryKeyCol, - j, - numberOfBytes = NUMBEROFBYTES(columnCount), - type, - offset, - crc32, // juliana@230_12 - oldPos, - k; // juliana@270_23 - bool changePos, - addingNewRecord = recPos == -1, - valueOk, - hasIndex, - isNullVOld, - isNull; - int8* columnTypes = table->columnTypes; - int32* columnSizes = table->columnSizes; - uint16* columnOffsets = table->columnOffsets; - uint8* basbuf = plainDB->basbuf; - uint8* columnNulls0 = table->columnNulls; - uint8 columnNulls1[NUMBEROFBYTES(MAXIMUMS + 1)]; - uint8* storeNulls = table->storeNulls; - uint8* columnAttrs = table->columnAttrs; - uint8* buffer = basbuf + table->columnOffsets[columnCount]; - SQLValue** defaultValues = table->defaultValues; - SQLValue* vOlds[MAXIMUMS + 1]; - SQLValue* tempRecord; - Index* idx; - Index** columnIndexes = table->columnIndexes; - Key tempKey; - SQLValue value; - - xmemzero(columnNulls0, numberOfBytes); // First of all, clear the columnNulls used. - - while (--i) // 0 = rowid = never is null. - { - // juliana@226_11: corrected a constant Java String truncation when using it with an insert or update prepared statement and its size were - // bigger than the column definition. - // juliana@225_7: a PrimaryKeyViolation was not being thrown when two strings with the same prefix were inserted and the field definition had - // the size of the prefix and a primary key. - // juliana@214_6: must trim strings during index update if they are longer than the field size definition. - if (columnSizes[i] && (tempRecord = values[i]) && (int32)tempRecord->length > (j = columnSizes[i])) - tempRecord->length = j; - - if (isBitSet(storeNulls, i)) // If not explicit to store null. - setBitOn(columnNulls0, i); - else if (addingNewRecord) - { - if (!definedAsNotNull(columnAttrs[i])) // Can be null. - { - if (!values[i] || values[i]->isNull) - { - if (!defaultValues[i]) // It doesn't have a default value. - setBitOn(columnNulls0, i); // Sets the column as null. - else - values[i] = defaultValues[i]; // If it doesn't have a value, stores the default value. - } - } - else if (!values[i]) // At this moment, if it can't be null, necessarily it has a default value. - values[i] = defaultValues[i]; - } - } - - // If there is a primary key column, there can't be repeated values. - if (primaryKeyCol != NO_PRIMARY_KEY && values[primaryKeyCol] - && !checkPrimaryKey(context, table, &values[primaryKeyCol], recPos, addingNewRecord, heap)) - return false; - if ((i = table->numberComposedPKCols) > 0) - { - uint8* composedPrimaryKeyCols = table->composedPrimaryKeyCols; - - while (--i >= 0) - vOlds[i] = values[composedPrimaryKeyCols[i]]; - if (!checkPrimaryKey(context, table, vOlds, recPos, addingNewRecord, heap)) - return false; // guich@564_18: changed from -1 to 0. - xmemzero(vOlds, columnCount * TSIZE); - } - - if (addingNewRecord) // Adding a record? - { - if (!plainAdd(context, plainDB)) - return false; - writePos = plainDB->rowCount; - (*values)->asInt = rowid = table->currentRowId; // Writes the rowId, marking the attribute as new. - if (!resetAuxRowId(context, table)) - return false; - } - else - { - if (!plainRead(context, plainDB, recPos)) // May have to read the value before deleting the index value. - return false; - writePos = recPos; - xmove4(&rowid, basbuf); - table->wasUpdated = true; // Indicates if the table was updated after the last time it was opened. - } - - tempKey.keys = &value; - - i = -1; - - IF_HEAP_ERROR(table->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - // Verifies if the value to be read is null. IMPORTANT: the array of bytes can't be stored in table.columnNulls[0]. This variable is in use. - // Then, it will be read in Table.columnNulls[1]. - xmemmove(columnNulls1, buffer, numberOfBytes); - - while (++i < columnCount) - { - isNullVOld = false; - isNull = isBitSet(columnNulls0, i); - idx = columnIndexes[i]; // If a new value is being written, the table index (if any) needs to be updated. - hasIndex = (valueOk = (values[i] || isNull)) && idx; // Only if this row is being updated. - type = columnTypes[i]; - offset = columnOffsets[i]; - changePos = false; - - if (!addingNewRecord) // Reads the previous value if it is an update. - { - vOlds[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - - // juliana@230_12 - // The offset is already positioned and is restored after read. - if (!readValue(context, plainDB, vOlds[i], offset, type, basbuf, false, isNullVOld = isBitSet(columnNulls1, i), false, -1, heap)) - return false; - - // juliana@238_5: corrected a possible table corruption when using blobs. - // juliana@202_19: UPDATE could corrupt .dbo. - // juliana@202_21: Always writes the string at the end of the .dbo. This removes possible bugs when doing updates. - // A blob in the .dbo must have its position changed if the the new value is greater than the old one. - if (valueOk && type == BLOB_TYPE && values[i] && vOlds[i]->length < values[i]->length) - changePos = true; - } - - // Writes the value. - if (!writeValue(context, plainDB, values[i], &basbuf[offset], type, columnSizes[i], valueOk, addingNewRecord || changePos, isNull, false)) - return false; - - // If the new and the old values are null and the column is not the rowid, sets the value as null. - if (i > 0 && isNullVOld && (!values[i] || values[i]->isNull)) - setBitOn(columnNulls0, i); - - if (hasIndex) - { - // juliana@225_4: corrected a bug that could not build the index correctly if there was the value 0 inserted in the index. - // Updating key? Removes the old one and adds the new one. - if (addingNewRecord || valueCompareTo(null, vOlds[i], values[i], type, isNullVOld, isNull, null)) - { - if (!isNull) // If it is updating a 'non-null value' to 'null value', only removes it. - { - oldPos = db->position; - tempRecord = values[i]; - IF_HEAP_ERROR(idx->heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - if (!indexAddKey(context, idx, &tempRecord, writePos)) // juliana@223_14: solved possible memory problems. - return false; - nfSetPos(db, oldPos); - } - if (!addingNewRecord && !isNullVOld) - { - oldPos = dbo->position; // juliana@202_19: UPDATE could corrupt .dbo. - tempRecord = vOlds[i]; - keySet(&tempKey, &tempRecord, idx, 1); - if (!indexRemoveValue(context, &tempKey, writePos)) - return false; - dbo->position = oldPos; // juliana@202_19: UPDATE could corrupt .dbo. - } - } - } - } - - if ((j = i = table->numberComposedIndexes)) // Fills the composed indices. - { - ComposedIndex* compIndex; - ComposedIndex** composedIndexes = table->composedIndexes; - Index* index; - SQLValue* vals[MAXIMUMS]; - SQLValue* oldVals = null; - uint8* columns; - int32 maxNumberColumns = 0, - column; - bool remove, // juliana@230_43 - change; // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - - // Allocates the records for the composed indices just once, using the maximum size. - while (--j >= 0) - maxNumberColumns = MAX(maxNumberColumns, composedIndexes[j]->numberColumns); - - if (!addingNewRecord) - oldVals = (SQLValue*)TC_heapAlloc(heap, maxNumberColumns * sizeof(SQLValue)); - - // If a composed index has all column values equal to null, it is not possible to store this row in the index. Composed indices that have at - // least one field that is not null can be stored in the index, but the null values must be handled. This implies in changing the index format. - // Maybe using the same way like null values are handled on tables. This certainly will decrease the index performance. For simplicity, the key - // will be stored in a composed index only if all values are not null. This is a project choice. - while (--i >= 0) - { - compIndex = composedIndexes[i]; - index = compIndex->index; - j = compIndex->numberColumns; - columns = compIndex->columns; - valueOk = remove = true; - change = false; // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - oldPos = db->position; // juliana@201_4: corrected a bug that could corrupt the table when updating the composed index. - - while (--j >= 0) - { - column = columns[j]; - if (isBitSet(columnNulls0, column)) // Only stores non-null values. - valueOk = false; - - // juliana@230_43: solved a possible exception if updating a table with composed indices and nulls. - if (isBitSet(columnNulls1, column)) - remove = false; - - // Sets the old and new index values. - // juliana@282_2: Doesn't update the composed index or PK if the old values are the same of the new ones. - if (values[column]) // juliana@201_18: can't reuse values. Otherwise, it will spoil the next update. - { - vals[j] = values[column]; - if (addingNewRecord || valueCompareTo(null, values[column], vOlds[column], columnTypes[column], !remove, !valueOk, null)) - change = true; - } - else - vals[j] = vOlds[column]; - if (!addingNewRecord) - xmemmove(&oldVals[j], vOlds[column], sizeof(SQLValue)); - } - - // juliana@230_43: solved a possible exception if updating a table with composed indices and nulls. - // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - if (!addingNewRecord && remove && change) // Removes the old composed index entry. - { - tempKey.index = index; - tempKey.record = NO_VALUE; - tempKey.keys = oldVals; - if (!indexRemoveValue(context, &tempKey, writePos)) - return false; - } - - // juliana@252_2: corrected a bug of possible composed index corruption when updating or deleting data. - if (valueOk && change) // juliana@201_4: corrected a bug that could corrupt the table when updating the composed index. - { - if (!indexAddKey(context, index, vals, writePos)) - return false; - nfSetPos(db, oldPos); - } - } - } - - xmemmove(buffer, columnNulls0, numberOfBytes); // After the columns, stores the bytes of the null values. - - // juliana@220_4: added a crc32 code for every record. - k = basbuf[3]; // juliana@270_23: Corrected a RowIterator bug of an update changing an updated row to synced again. - basbuf[3] = 0; // juliana@222_5: The crc was not being calculated correctly for updates. - i = columnOffsets[columnCount] + numberOfBytes; - - // juliana@230_12: improved recover table to take .dbo data into consideration. - crc32 = updateCRC32(basbuf, i, 0); - - if (table->version == VERSION_TABLE) - { - int32 length; - - j = columnCount; - while (--j) - if (columnTypes[j] == CHARS_TYPE || columnTypes[j] == CHARS_NOCASE_TYPE) - { - if (values[j] && isBitUnSet(columnNulls0, j)) - crc32 = updateCRC32((uint8*)values[j]->asChars, values[j]->length << 1, crc32); - else if (!addingNewRecord && isBitUnSet(columnNulls0, j) && !vOlds[j]->isNull && vOlds[j]->asChars) - crc32 = updateCRC32((uint8*)vOlds[j]->asChars, vOlds[j]->length << 1, crc32); - } - else if (columnTypes[j] == BLOB_TYPE) - { - if (values[j] && isBitUnSet(columnNulls0, j)) - { - // juliana@253_12: corrected a possible table corruption when adding a small blob. - // juliana@239_4: corrected a non-desired possible row delete when recovering a table with blobs. - length = MIN((uint32)columnSizes[j], values[j]->length); - crc32 = updateCRC32((uint8*)&length, 4, crc32); - } - else if (!addingNewRecord && isBitUnSet(columnNulls0, j) && !vOlds[j]->isNull) - { - length = vOlds[j]->length; - crc32 = updateCRC32((uint8*)&length, 4, crc32); - } - } - } - xmove4(&basbuf[i], &crc32); - basbuf[3] = k; // juliana@270_23: Corrected a RowIterator bug of an update changing an updated row to synced again. - - if (rowid > 0) // Now the record's attribute has to be updated. - { - int32 id = addingNewRecord? (rowid & ROW_ID_MASK) | ROW_ATTR_NEW : rowUpdated(rowid); - xmove4(basbuf, &id); - } - - // Writes the row. - if (!(addingNewRecord? plainWrite(context, plainDB) : plainRewrite(context, plainDB, writePos))) - return false; - - if (addingNewRecord) - table->currentRowId = rowid + 1; - - // juliana@227_3: improved table files flush dealing. - // juliana@202_23: Flushs the files to disk when row increment is the default. - // juliana@270_25: corrected a possible lose of records in recover table when 10 is passed to LitebaseConnection.setRowInc(). - // Flushs .db and .dbo. - if (!db->dontFlush) - if ((db->cacheIsDirty && !flushCache(context, db)) || (dbo->cacheIsDirty && !flushCache(context, dbo))) - return false; - - return true; -} - -/** - * Writes a record from an array of values in a result set. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param values The record to be written in the result set table. - * @return false if an error occurs; true, otherwise. - */ -bool writeRSRecord(Context context, Table* table, SQLValue** values) -{ - TRACE("writeRSRecord") - int32 n = table->columnCount; - PlainDB* plainDB = &table->db; - uint8* basbuf = plainDB->basbuf; - int32* sizes = table->columnSizes; - int8* types = table->columnTypes; - uint8* nulls = table->columnNulls; - uint16* offsets = table->columnOffsets; - - if (!plainAdd(context, plainDB)) // Adds a new row to the result set table. - return false; - - xmemmove(&basbuf[offsets[n]], nulls, NUMBEROFBYTES(n)); // Writes the null values. - - // Writes the columns into a temporary buffer. - while (--n >= 0) - if (!writeValue(context, plainDB, values[n], &basbuf[offsets[n]], types[n], sizes[n], values[n] != null, true, - nulls[n >> 3] & (1 << (n & 7)), true)) - return false; - return plainWrite(context, plainDB); // Finally, writes the row. -} - -/** - * Checks if a primary key constraint was violated - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param values The values inserted in the table. - * @param recPos The position of vals record. - * @param newRecord Indicates if it is an inserted or an updated record. - * @param heap A heap to allocate values read from the table. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If a member of the primary key is null. - * @throws PrimaryKeyViolation If a there is a repeated primary key. - */ -bool checkPrimaryKey(Context context, Table* table, SQLValue** values, int32 recPos, bool newRecord, Heap heap) -{ - TRACE("checkPrimaryKey") - Key tempKey; - int32 primaryKeyCol = table->primaryKeyCol, // Simple primary key column. - size, - i; - bool hasChanged = false; - SQLValue oldValues[MAXIMUMS + 1]; - Index* index; - uint8* columns; - - if (primaryKeyCol == -1) - { - columns = table->composedIndexes[table->composedPK]->columns; // Gets the columns of the index. - i = size = table->numberComposedPKCols; - index = table->composedIndexes[table->composedPK]->index; - } - else - { - columns = (uint8*)&primaryKeyCol; // Gets the column of the index. - i = size = 1; - index = table->columnIndexes[primaryKeyCol]; - } - - xmemzero(oldValues, size * sizeof(SQLValue)); - if (!newRecord) // An update. - { - PlainDB* plainDB = &table->db; - int8* types = index->types; - uint16* offsets = table->columnOffsets; - uint8* basbuf = plainDB->basbuf; - - if (!plainRead(context, plainDB, recPos)) // Reads the table row. - return false; - while (--i >= 0) - { - // juliana@220_3 juliana@230_12 - // If it is updating a record, reads the old value and checks if a primary key value has changed. - if (!readValue(context, plainDB, &oldValues[i], offsets[columns[i]], types[i], basbuf, false, false, false, -1, heap)) - return false; - - // Tests if the primary key has not changed. - if (values[i] && valueCompareTo(null, &oldValues[i], values[i], types[i], false, false, null)) - hasChanged = true; - - if (!values[i]) // Uses the old value. - values[i] = &oldValues[i]; - } - i = size; - } - - while (--i >= 0) // There can't be a null in a primary key. - if (!values[i]) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_PK_CANT_BE_NULL), 0); - return false; - } - - if (hasChanged || newRecord) // Sees if the record does not violate the primary key. - { - tempKey.keys = oldValues; - keySet(&tempKey, values, index, size); - return indexGetValue(context, &tempKey, null); - } - return true; -} - -/** - * Verifies the null and default values of a statement. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param record The record to be inserted or updated. - * @param statementType The type of the statement, which can be SQLElement.STMT_INSERT or SQLElement.CMD_UPDATE. - * @param nValues The number of values being verified. - * @return false if a null violation occurs; true, otherwise. - * @throws DriverException If a primary key is or a NOT NULL field is is null. - */ -bool verifyNullValues(Context context, Table* table, SQLValue** record, int32 statementType, int32 nValues) -{ - TRACE("verifyNullValues") - int32 i = table->primaryKeyCol; - uint8* storeNulls = table->storeNulls; - uint8* attrs = table->columnAttrs; - - if (statementType == CMD_INSERT) // Insert statement. - { - SQLValue** defaultValues = table->defaultValues; - - // The primary key can't be null. - if ((i != NO_PRIMARY_KEY) && (isBitSet(storeNulls, i) || ((!record[i] || record[i]->isNull) && !defaultValues[i]))) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_PK_CANT_BE_NULL)); - return false; - } - - i = table->columnCount; - while (--i) - if ((!record[i] || record[i]->isNull) && !defaultValues[i] && definedAsNotNull(attrs[i])) // A not null field can't have a null. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_FIELD_CANT_BE_NULL), table->columnNames[i]); - return false; - } - } - else // Update statement. - { - if ((i != NO_PRIMARY_KEY) && (isBitSet(storeNulls, i))) // The primary key can't be null. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_PK_CANT_BE_NULL)); - return false; - } - i = nValues; - while (--i >= 0) - if (isBitSet(storeNulls, i) && definedAsNotNull(attrs[i])) // If it is to store a null but a null can't be stored. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_FIELD_CANT_BE_NULL), table->columnNames[i]); - return false; - } - } - return true; -} - -// juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. -/** - * Converts the strings of the record into the real values, accordingly to the given table column types. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param record The record whose strings are to be transformed in their real types. - * @param nValues The number of values in the record. - * @return false if an error occurs; true, otherwise. - * @throws SQLParseException If a conversion from string to a number or date/datetime fails. - * @throws DriverException If a blob is passed in a statement that is not prepared. - */ -bool convertStringsToValues(Context context, Table* table, SQLValue** record, uint32 nValues) -{ - TRACE("convertStringsToValues") - DoubleBuf buffer; // greatest type - int8* columnTypes = table->columnTypes; - bool error = false; - int32 type, - length; - JCharP asChars; - CharP strVal; - SQLValue* value; - - while (--nValues) // 0 = rowid. - { - // If the column is storing a null, the string is considered to be null. - asChars = (value = record[nValues])? value->asChars : null; - - // A blob can't be set in a normal statement (if the string is not null, it won't be a "?". - if ((type = columnTypes[nValues]) == BLOB_TYPE && asChars && (asChars[0] != '?' || asChars[1])) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_BLOBS_PREPARED)); - return false; - } - - // Ignores null values, blobs or unset parameters. - if ((!asChars || (asChars[0] == '?' && !asChars[1])) || type == BLOB_TYPE) - continue; - - if (type != CHARS_TYPE && type != CHARS_NOCASE_TYPE) - { - if ((length = value->length) > 39) - { - strVal = TC_JCharP2CharP(asChars, length); - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), strVal? strVal : "", "number"); - xfree(strVal); - return false; - } - strVal = TC_JCharP2CharPBuf(asChars, length, buffer); - - switch (type) - { - case SHORT_TYPE: - value->asShort = str2short(strVal, &error); - break; - - case INT_TYPE: - value->asInt = TC_str2int(strVal, &error); - break; - - case LONG_TYPE: - value->asLong = TC_str2long(strVal, &error); - break; - - case FLOAT_TYPE: - value->asFloat = str2float(strVal, &error); - break; - - case DOUBLE_TYPE: - value->asDouble = TC_str2double(strVal, &error); - break; - - case DATE_TYPE: // rnovais@567_2 - case DATETIME_TYPE: // rnovais@567_2 - if (!testAndPrepareDateAndTime(context, value, strVal, type)) - return false; - } - if (error) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), strVal, "number"); - return false; - } - } - } - return true; -} - -// juliana@230_12: improved recover table to take .dbo data into consideration. -// juliana@253_8: now Litebase supports weak cryptography. -/** - * Updates the CRC32 value with the values of the given buffer. - * - * @param buffer The buffer. - * @param length The number of bytes to be used to update the CRC code. - * @param oldCRC The previous CRC32 value. - * @return The CRC32 code updated to include the buffer data. - */ -int32 updateCRC32(uint8* buffer, int32 length, int32 oldCRC) -{ - TRACE("computeCRC32") - oldCRC = ~oldCRC; - - while (--length >= 0) - oldCRC = crcTable[(oldCRC ^ *buffer++) & 0xff] ^ (((uint32)oldCRC) >> 8); - return ~oldCRC; -} - -/** - * Makes the table for a fast CRC. - */ -void make_crc_table(void) -{ - TRACE("make_crc_table") - int32 n = 256, - c, - k; - - while (--n >= 0) - { - c = n; - k = 8; - while (--k >= 0) - { - if ((c & 1) != 0) - c = 0xedb88320 ^ (((uint32)c) >> 1); - else - c = (((uint32)c) >> 1); - } - crcTable[n] = c; - } -} - -/** - * Resets the auxiliary rowid. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @return false if an error occurs; true, otherwise. - */ -bool resetAuxRowId(Context context, Table* table) // rnovais@570_61 -{ - TRACE("resetAuxRowId") - if (table->auxRowId != ATTR_DEFAULT_AUX_ROWID) - { - XFile* db = &table->db.db; - int32 pos = db->position; - - table->auxRowId = ATTR_DEFAULT_AUX_ROWID; - if (!tableSaveMetaData(context, table, TSMD_ONLY_AUXROWID)) - return false; - nfSetPos(db, pos); - return true; - } - return true; -} - -/** - * Changes the state of a row to updated. - * - * @param id The rowid to have its atribute changed. - * @return The rowid with its atribute changed to updated. - */ -int32 rowUpdated(int32 id) -{ - TRACE("rowUpdated") - if ((id & ROW_ATTR_MASK) == ROW_ATTR_SYNCED) - return (id & ROW_ID_MASK) | ROW_ATTR_UPDATED; - return id; -} - -/** - * Frees a table when closing a Litebase connection. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @return false if an error occurs; true, otherwise. - */ -bool freeTableHT(Context context, Table* table) -{ - TRACE("freeTableHT") - return freeTable(context, table, 0, true); -} - -/** - * Closes a table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param isDelete Indicates if the table is to be deleted. - * @param updatePos Indicates if the .db file is to be truncated or not. - * @return false if an error occurs; true, otherwise. - */ -bool freeTable(Context context, Table* table, bool isDelete, bool updatePos) -{ - TRACE("freeTable") - bool ret = true; - - if (table) - { - int32 n = table->columnCount, - i; - Index** columnIndexes = table->columnIndexes; - ComposedIndex** composedIndexes = table->composedIndexes; - Index* idx; - TCObject obj; - TCObjects* list = table->preparedStmts; - TCObjects* preparedStmts = table->preparedStmts; - TCHARP sourcePath = table->sourcePath; - - TC_htFree(&table->htName2index, null); // Frees the column names hash table. - xfree(table->allRowsBitmap); // juliana@230_14 - - if (table->columnIndexes) // Frees the simple indices in a normal table. - while (--n >= 0) - { - if ((idx = columnIndexes[n])) - { - if (!indexClose(context, idx)) - ret = false; - columnIndexes[n] = null; - } - } - - if (*table->name && (i = table->numberComposedIndexes)) // Frees the composed indices in a normal table. - { - while (--i >= 0) - { - if (composedIndexes[i] && (idx = composedIndexes[i]->index)) - { - if (!indexClose(context, idx)) - ret = false; - composedIndexes[i]->index = null; - } - } - } - - if (isDelete) // Frees or removes the table. - ret &= plainRemove(context, &table->db, sourcePath); - else - ret &= plainClose(context, &table->db, updatePos); - - // juliana@221_1: solved a problem that could reduce the free memory too much if many prepared statements were created and collected many - // times. - if (preparedStmts) - { - Hashtable* htPS; - TCObject sqlObj; - do - { - // juliana@226_16: prepared statement is now a singleton. - if ((obj = list->value)) - { - htPS = getLitebaseHtPS(OBJ_PreparedStatementDriver(obj)); - sqlObj = OBJ_PreparedStatementSqlExpression(obj); - TC_htRemove(htPS, TC_JCharPHashCode(String_charsStart(sqlObj), String_charsLen(sqlObj))); - freePreparedStatement(0, obj); - } - list = TC_TCObjectsRemove(list, obj); - list = list->next; - } while (preparedStmts != list); - } - heapDestroy(table->heap); - } - return ret; -} - -/** - * Gets the value of a column of the underlying table used by the result set. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set whose table will be read. - * @param column The number of the column from which the value will be fetched. - * @param value The structure where the value will be stored. - * @return false if an error occurs; true, otherwise. - */ -bool getTableColValue(Context context, ResultSet* resultSet, int32 column, SQLValue* value) -{ - TRACE("getTableColValue") - Table* table = resultSet->table; - - // juliana@230_12 - return readValue(context, &table->db, value, table->columnOffsets[column], table->columnTypes[column], table->db.basbuf, !*table->name, - isBitSet(table->columnNulls, column), true, table->columnSizes[column], null); -} - -/** - * Indicates if a table already exists on disk or not. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param name The table name. - * @return true if the table already exists; false, othewise. - * @throws AlreadyCreatedException If the table is already created. - * @throws DriverException If the path is too long. - */ -bool tableExistsByName(Context context, TCObject driver, CharP name) -{ - TRACE("tableExistsByName") - TCHAR fullName[MAX_PATHNAME]; - char bufName[DBNAME_SIZE]; - TCHARP sourcePath = getLitebaseSourcePath(driver); - - // juliana@252_3: corrected a possible crash if the path had more than 255 characteres. - if (xstrlen(name) + tcslen(sourcePath) + 10 > MAX_PATHNAME) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PATH), sourcePath); - return false; - } - - // Verifies if the table exists checking if the .db exists. The name must be already on lower case. - if (!getDiskTableName(context, OBJ_LitebaseAppCrid(driver), name, bufName)) - return true; - getFullFileName(bufName, sourcePath, fullName); - tcscat(fullName, TEXT(DB_EXT)); - - if (lbfileExists(fullName)) - { - // The .db file already exists. So, the table is considered to exists. - TC_throwExceptionNamed(context, "litebase.AlreadyCreatedException", getMessage(ERR_TABLE_ALREADY_CREATED), name); - return true; - } - return false; -} - -/** - * Gets the table name on disk. - * - * @param context The thread context where the function is being executed. - * @param crid The application id for the connection. - * @param name The table name. - * @param buffer The buffer for the table name on disk. - * @return false if table name is too big; true, otherwise. - * @throws DriverException If the table name is too big. - */ -bool getDiskTableName(Context context, int32 crid, CharP name, CharP buffer) -{ - TRACE("getDiskTableName") - CharP to = &buffer[5]; - - if (xstrlen(name) > MAX_TABLE_NAME_LENGTH) // The table name can't be too big because of palm os. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - return false; - } - - // Gets the application id as a string. - TC_int2CRID(crid, buffer); - buffer[4] = '-'; - - while (*name) // guich@553_9: converts to lowercase. - *to++ = TC_toLower(*name++); - *to = 0; - return true; -} - -/** - * Given a table name as an object, returns its table. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param name The table name as a string object. - * @return null if an error occurs; a table handle, otherwise. - * @throws DriverException If the table name is too big. - */ -Table* getTableFromName(Context context, TCObject driver, TCObject name) -{ - TRACE("getTableFromName") - char tableName[DBNAME_SIZE]; - int32 length = String_charsLen(name); - - if (length > MAX_TABLE_NAME_LENGTH) // The table name can't be too big because of palm os. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - return null; - } - - TC_JCharP2CharPBuf(String_charsStart(name), length, tableName); - TC_CharPToLower(tableName); - return getTable(context, driver, tableName); -} - -/** - * Given a table name as an uint8 string, returns its table. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param tableName The table name as an uint8 string. - * @return null if an error occurs; a table handle, otherwise. - * @throws DriverException If the table name is too big. - * @throws OutOfMemoryError If there is not enougth memory to be allocated. - */ -Table* getTable(Context context, TCObject driver, CharP tableName) -{ - TRACE("getTable") - char name[DBNAME_SIZE]; - Table* table = null; - Hashtable* htTables = getLitebaseHtTables(driver); - int32 length = xstrlen(tableName), - appCrid = OBJ_LitebaseAppCrid(driver), - hashCode; - - if (length > MAX_TABLE_NAME_LENGTH) // The table name can't be too big because of palm os. - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_MAX_TABLE_NAME_LENGTH)); - return null; - } - - TC_CharPToLower(tableName); // The hash code must be in lower case so that the program is more efficient. - - // Opens a not already opened table. - if (!(table = (Table*)TC_htGetPtr(htTables, hashCode = TC_hashCode(tableName)))) - { - // Gets the table name. - if (getDiskTableName(context, appCrid, tableName, name)) - { - Heap heap = heapCreate(); - - IF_HEAP_ERROR(heap) - { - heapDestroy(heap); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - heap->greedyAlloc = true; - - // Opens it. It must have been already created. - // juliana@220_5 - // juliana@253_8: now Litebase supports weak cryptography. - if ((table = tableCreate(context, name, getLitebaseSourcePath(driver), false, OBJ_LitebaseIsAscii(driver), OBJ_LitebaseUseCrypto(driver), - getLitebaseNodes(driver), true, heap)) && table->db.db.size) - { - if (!TC_htPutPtr(htTables, hashCode, table)) // Puts the table hash code in the hash table of opened tables. - { - freeTable(context, table, false, false); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - } - else - return null; - } - } - return table; -} - -/** - * Reads a string from a buffer. - * - * @param buffer The buffer being read. - * @param string The string returned. - * @param heap The heap where the string will be allocated. - * @return The buffer remaning after reading the string. - */ -uint8* readString(uint8* buffer, CharP* string, Heap heap) -{ - TRACE("readString") - uint32 length = 0; - - xmove2(&length, buffer); - xmemmove((*string = (CharP)TC_heapAlloc(heap, length + 1)), buffer += 2, length); - return buffer + length; -} - -/** - * Reads a string array from a buffer. - * - * @param buffer The buffer being read. - * @param strings The string array returned. - * @param count The column count. - * @param heap The heap where the string array will be allocated. - * @return The buffer remaning after reading the string array. - */ -uint8* readStringArray(uint8* buffer, CharP** strings, int32 count, Heap heap) -{ - TRACE("readStringArray") - int32 i = -1; - - buffer += 2; - *strings = (CharP*)TC_heapAlloc(heap, count * TSIZE); - while (++i < count) - buffer = readString(buffer, &((*strings)[i]), heap); - return buffer; -} - -/** - * Writes a string to a buffer. - * - * @param buffer The buffer being written. - * @param string The string to be written. - * @return The buffer remaning after writing the string. - */ -uint8* writeString(uint8* buffer, CharP string) -{ - TRACE("writeString") - uint32 length = xstrlen(string); - - xmove2(buffer, &length); - xmemmove(buffer + 2, string, length); - return buffer + length + 2; -} - -/** - * Writes a string array to a buffer. - * - * @param buffer The buffer being written. - * @param strings The string array to be written. - * @param count The column count. - * @return The buffer remaning after writing the string array. - */ -uint8* writeStringArray(uint8* buffer, CharP* strings, int32 count) -{ - TRACE("writeStringArray") - int32 i = -1; - - buffer += 2; - while (++i < count) - buffer = writeString(buffer, strings[i]); - return buffer; -} - -/** - * Writes a unicode string to a buffer. - * - * @param buffer The buffer being written. - * @param string The string to be written. - * @param lengtg The string length. - * @return The buffer remaning after writing the string. - */ -uint8* writeString16(uint8* buffer, JCharP string, int32 length) -{ - TRACE("writeString16") - xmove2(buffer, &length); - xmemmove(buffer + 2, string, length <<= 1); - return buffer + length + 2; -} - -/** - * Changes a table to the modified state whenever it is modified. - * - * @param context The thread context where the function is being executed. - * @param table The table to be set as modified. - * @return false if an error occurs; true, otherwise. - */ -bool setModified(Context context, Table* table) -{ - TRACE("setModified") - - if (!table->isModified) - { - PlainDB* plainDB = &table->db; - XFile* dbFile = &plainDB->db; - int32 isAscii; - - isAscii = (plainDB->isAscii? IS_ASCII : 0); - nfSetPos(dbFile, 6); - if (nfWriteBytes(context, dbFile, (uint8*)&isAscii, 1) && flushCache(context, dbFile)) // Flushs .db. - table->isModified = true; - else - return false; - } - return true; -} - - -int32 randBetween(int32 low, int32 high) -{ - TRACE("randBetween") - - if (low == high) - high++; - return low + (((uint32)rand()) % (high - low + 1)); -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if rowUpdated() works correctly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(rowUpdated) -{ - UNUSED(currentContext) - ASSERT2_EQUALS(I32, 2147483648U, rowUpdated(0)); - ASSERT2_EQUALS(I32, 2147483658U, rowUpdated(10)); - ASSERT2_EQUALS(I32, 2147483748U, rowUpdated(100)); - ASSERT2_EQUALS(I32, 2147484648U, rowUpdated(1000)); - ASSERT2_EQUALS(I32, 2147493648U, rowUpdated(10000)); - ASSERT2_EQUALS(I32, 2147583648U, rowUpdated(100000)); - ASSERT2_EQUALS(I32, 2148483648U, rowUpdated(1000000)); - ASSERT2_EQUALS(I32, 2157483648U, rowUpdated(10000000)); - ASSERT2_EQUALS(I32, 2247483648U, rowUpdated(100000000)); - ASSERT2_EQUALS(I32, 3147483648U, rowUpdated(1000000000)); - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/Table.h b/LitebaseSDK/src/native/Table.h deleted file mode 100644 index 099de33bb4..0000000000 --- a/LitebaseSDK/src/native/Table.h +++ /dev/null @@ -1,639 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions to manipulate table structures. - */ - -#ifndef LITEBASE_TABLE_H -#define LITEBASE_TABLE_H - -#include "Litebase.h" - -/** - * Verifies if the index already exists. - * - * @param context The thread context where the function is being executed. - * @param table The table whose index is to be created. - * @param columnNumbers The columns that are part of this index. - * @param indexCount The number of columns of the index. - * @return 0 for simple indices. For composed index, if there was this same index, it returns the negative number of the this old one; otherwise, - * it returns the new number. - * @throws AlreadyCreatedException If an index already exists. - */ -int32 verifyIfIndexAlreadyExists(Context context, Table* table, uint8* columnNumbers, int32 indexCount); - -/** - * Drops an index. - * - * @param context The thread context where the function is being executed. - * @param table The table whose index is to dropped. - * @param column The column of the index dropped. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the column does not have an index. - */ -bool driverDropIndex(Context context, Table* table, int32 column); - -/** - * Drops a composed index. - * - * @param context The thread context where the function is being executed. - * @param table The table whose index is to dropped. - * @param columns The columns of the composed index. - * @param size The number of columns of the composed index. - * @param indexId The id of the composed index or -1 if its position is not known. - * @param saveMD Indicates if the meta data is to be saved. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the table does not have the desired composed index to be dropped. - */ -bool driverDropComposedIndex(Context context, Table* table, uint8* columns, int32 size, int32 indexId, bool saveMD); - -/** - * Deletes all indices of a table. - * - * @param context The thread context where the function is being executed. - * @param table The table whose indices are to dropped. - * @return The number of indices deleted. - */ -int32 deleteAllIndexes(Context context, Table* table); - -/** - * Computes the column offsets of the table columns. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If there is not enougth memory to be allocated. - */ -bool computeColumnOffsets(Context context, Table* table); - -/** - * Loads the meta data of a table, - * - * @param context The thread context where the function is being executed. - * @param table The table being loaded. - * @param throwException Indicates that a TableNotClosedException should be thrown. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the table is in an incompatible format. - * @throws TableNotClosedException If the table was not properly close when opened last time. - */ -bool tableLoadMetaData(Context context, Table* table, bool throwException); - -/** - * Saves the table meta data - * - * @param context The thread context where the function is being executed. - * @param table The table whose meta data is being saved. - * @param saveType The kind of save. It can be one out of TSMD_ONLY_DELETEDROWSCOUNT, - * TSMD_ONLY_PRIMARYKEYCOL, TSMD_EVERYTHING, or TSMD_ONLY_AUXROWID. - * @throws OutOfMemoryError If there is not enougth memory to be allocated - */ -bool tableSaveMetaData(Context context, Table* table, int32 saveType); - -/** - * Sets the meta data for a table. - * - * @param context The thread context where the function is being executed. - * @param table The table being set. - * @param names The table column names. - * @param hashes The table column names hash codes. - * @param types The table column types. - * @param sizes The table column sizes. - * @param attrs The table column attributtes. - * @param composedPKCols The table primary key column. - * @param defaultValues The default values of the table columns. - * @param primaryKeyCol The table primary key column. - * @param composedPK The composed primary key index in the composed indices. - * @param columnCount The number of columns of the table. - * @param ComposedPKColsSize The number of composed primary keys. - * @return false if an error occurs; true, otherwise. - * @throws AlreadyCreatedException if the table is already created. - */ -bool tableSetMetaData(Context context, Table* table, CharP* names, int32* hashes, int8* types, int32* sizes, uint8* attrs, uint8* composedPKCols, - SQLValue** defaultValues, int32 primaryKeyCol, int32 composedPK, int32 columnCount, int32 composedPKColsSize); - -/** - * Gets the table standart metadata size to save a table. - * - * @param table The table to be saved. - * @param saveType The save type of the table, which increases or decreases its size. - * @return The metadata size. - */ -int32 getTSMDSize(Table* table, int32 saveType); - -/** - * Gets the total size of the table column names. - * - * @param names The names of the columns. - * @param count The number of colums. - * @return the total size for the column names. - */ -int32 getStringsTotalSize(CharP* names, int32 count); - -/** - * Gets the total size needed to store the table default values. - * - * @param table The table. - * @return the total size for the default values. - */ -int32 computeDefaultValuesMetadataSize(Table* table); - -/** - * Gets the total size needed to store the table composed indices. - * - * @param table The table. - * @return the total size for the composed indices. - */ -int32 computeComposedIndicesTotalSize(Table* table); - -/** - * Reorder the values of a statement to match the table definition. - * - * @param context The thread context where the function is being executed. - * @param table The table of the statement. - * @param fields The fields of the statement. - * @param record The record of values of the statement. - * @param storeNulls Indicates which values have a null. - * @param nValues The number of values. - * @param paramIndexes The indices of the parameters, if any, in the record. - * @return false if there is an invalid field name; true, otherwise. - * @throws DriverException if there is an invalid field name. - */ -bool reorder(Context context, Table* table, CharP* fields, SQLValue** record, uint8* storeNulls, uint8* nValues, uint8* paramIndexes); - -/** - * Sorts a table, using an ORDER BY or GROUP BY clause. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param groupByClause The group by clause. - * @param orderByClause The order by clause. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -bool sortTable(Context context, Table* table, SQLColumnListClause* groupByClause, SQLColumnListClause* orderByClause); - -/** - * Quick sort method used to sort the table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param pivot The pivot of this partition; - * @param someRecord1 An auxiliar record to avoid re-creating it. - * @param someRecord2 An auxiliar record to avoid re-creating it. - * @param fieldList The order of comparison of the fields. - * @param bufAux A buffer to store the records. - * @param first The first index of this partition. - * @param last The last index of this partition. - * @param fieldsCount The number of fields in the field list. - * @param heap A heap to allocate temporary sort structures. - * @return false if an error occurs; true, otherwise. - */ -bool quickSort(Context context, Table* table, SQLValue** pivot, SQLValue** someRecord1, SQLValue** someRecord2, SQLResultSetField** fieldList, - uint8* bufAux, int32 first, int32 last, int32 fieldsCount, Heap heap); - -/** - * Compares two records. Used for sorting the table to build the indices from scratch. - * - * @param recSize The size of the records being compared. - * @param vals1 The first record of the comparison. - * @param vals2 The second record of the comparison. - * @param types The types of the record values. - * @return A positive number if vals1 > vals2; 0 if vals1 == vals2; -1, otherwise. It will return MAX_INT_VALUE if both records are - * equal but the record of the first is greater than the second, and MIN_INT_VALUE if both records are equal but the record of the - * first is less than the second. - */ -int32 compareSortRecords(int32 recSize, SQLValue** vals1, SQLValue** vals2, int8* types); - -/** - * Quick sort used for sorting the table to build the indices from scratch. This one is simpler than the sort used for order / gropu by. - * Uses a stack instead of a recursion. - * - * @param sortValues The records to be sorted. - * @param recSize The size of the records being sorted. - * @param types The types of the record values. - * @param first The first element of current partition. - * @param last The last element of the current. - * @param vector A temporary array to use in the recursion. - */ -void sortRecords(SQLValue*** sortValues, int32 recSize, int8* types, int32 first, int32 last, int32* vector); - -/** - * Does a radix sort on the given SQLValue array. Only integral types are allowed (SHORT, INT, LONG). This is faster than quicksort. Also used to - * build the indices from scratch. - * - * @param source The values to be sorted. Only simple records for simple indices can be used. - * @param length The number of values to be sorted. - * @param type The type of the elements. - * @param temp A temporary array for the sort. - */ -void radixSort(SQLValue*** source, int32 length, int32 type, SQLValue*** temp); - -/** - * Executes a pass of the radix sort. - * - * @param start Start bit. - * @param source The source array, - * @param dest The dest array where the operations with the source are copied to. - * @param count A temporary array. - * @param index A temporary array. - * @param type The type of the values being sorted. - * @param length The number of values to be sorted. - * @return A number of bits. - */ -int64 radixPass(int32 start, SQLValue*** source, SQLValue*** dest, int32* count, int32* index, int32 type, int32 length); - -/** - * Creates the table files and loads its meta data if it was already created. - * - * @param context The thread context where the function is being executed. - * @param name The name of the table. - * @param sourcePath The path of the table on disk. - * @param create Indicates if the table is to be created or just opened. - * @param isAscii Indicates if the table strings are to be stored in the ascii format or in the unicode format. - * @param useCrypto Indicates if the table uses cryptography. - * @param nodes An array of nodes indices. - * @param throwException Indicates that a TableNotClosedException should be thrown. - * @param heap The table heap. - * @return The table created or null if an error occurs. - */ -Table* tableCreate(Context context, CharP name, TCHARP sourcePath, bool create, bool isAscii, bool useCrypto, int32* nodes, - bool throwException, Heap heap); - -/** - * Creates a table, which can be stored on disk or on memory (result set table). - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param tableName The table name. - * @param names The table column names. - * @param hashes The table column hashes. - * @param types The table column types. - * @param sizes The table column sizes. - * @param attrs The table cxlumn attributes. - * @param defaultValues The table column default values. - * @param primaryKeyCol The primary key column. - * @param composedPK The composed primary key index in the composed indices. - * @param composedPKCols The columnns that are part of the composed primary key. - * @param ComposedPKColsSize The size of the composed primary key. - * @param count The column count. - * @param heap A heap to allocate the table. - * @return The table handle or null if an error occurs. - * @throws AlreadyCreatedException If the table already exists. - * @throws OutOfMemoryError If an memory allocation fails. - */ -Table* driverCreateTable(Context context, TCObject driver, CharP tableName, CharP* names, int32* hashes, int8* types, int32* sizes, uint8* attrs, - SQLValue** defaultValues, int32 primaryKeyCol, int32 composedPK, uint8* composedPKCols, int32 composedPKColsSize, int32 count, Heap heap); - -/** - * Renames a table. This never happens to be a temporary ResultSet memory table. - * - * @param context The thread context where the function is being executed. - * @param driver The LitebaseConnection. - * @param table The table being renamed. - * @param newTableName The new table name. - * @return false if an error occurs; true, otherwise. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool renameTable(Context context, TCObject driver, Table* table, CharP newTableName); // rnovais@566_10 - -/** - * Renames a column of a table. - * - * @param context The thread context where the function is being executed. - * @param table The table whose column is being renamed. - * @param oldColumn The name of the old column. - * @param newColumn The name of the new column. - * @param reuseSpace Indicates if the column name space can be reused. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the old column does not exist or the new column already exists. - * @throws OutOfMemoryError If a memory allocation fails. - */ -bool renameTableColumn(Context context, Table* table, CharP oldColumn, CharP newColumn, bool reuseSpace); // rnovais@566_17 - -/** - * Re-builds an index of a table. - * - * @param context The thread context where the function is being executed. - * @param table The table being re-indexed. - * @param column The table column number of the index or -1 for a composed index. - * @param isPKCreation Indicates that the index is of a primary key. - * @param composedIndex The composed index to be rebuilt or null in case of a simple index. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If there is a null in the primary key or a duplicated key. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -bool tableReIndex(Context context, Table* table, int32 column, bool isPKCreation, ComposedIndex* composedIndex); - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * Creates a simple index for the table for the given column. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param fullTableName The table disk name. - * @param columnIndex The column of the index. - * @param columnSizes The sizes of the columns. - * @param columnTypes The types of the columns. - * @param exist Indicates that the index files already exist. - * @param heap A heap to allocate the index structure. - * @return false if an error occurs; true, otherwise. - */ -bool indexCreateIndex(Context context, Table* table, CharP fullTableName, int32 columnIndex, int32* columnSizes, int8* columnTypes, bool exist, - Heap heap); - -// juliana@noidr_1: removed .idr files from all indices and changed its format. -/** - * Creates a composed index for a given table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param fullTableName The table disk name. - * @param columnIndexes he columns of the index. - * @param columnSizes The sizes of the columns. - * @param columnTypes The types of the columns. - * @param numberColumns The number of columns of the index. - * @param newIndexNumber An id for the composed index. - * @param increaseArray Indicates if the composed indices array must be increased. - * @param exist Indicates that the index files already exist. - * @param heap A heap to allocate the index structure. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the maximum number of composed indices was achieved. - */ -bool indexCreateComposedIndex(Context context, Table* table, CharP fullTableName, uint8* columnIndexes, int32* columnSizes, int8* columnTypes, - int32 numberColumns, int32 newIndexNumber, bool increaseArray, bool exist, Heap heap); - -/** - * Reads the entire record from a table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param record An array where the record filed values will be stored. - * @param recPos The record index. - * @param columnNulls A buffer where the nulls will be stored. - * @param fieldList A field list that indicates which fields to read from the table. - * @param fieldsCount The number of fields in the field list. - * @param isTempBlob Indicates if a blob must be loaded or not. - * @param heap A heap to allocate the temporary strings when sorting a temporary table. - * @param stringArray A temporary string array used when sorting a temporary table. - * @return false if an error occurs; true, otherwise. - */ -bool readRecord(Context context, Table* table, SQLValue** record, int32 recPos, uint8* columnNulls, SQLResultSetField** fieldList, - int32 fieldsCount, bool isTempBlob, Heap heap, StringArray** stringArray); - -/** - * Writes a record on a disk table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param values The values to be written on the table. - * @param recPos The record position. - * @param heap A heap to allocate temporary structures. - * @return false if an error occurs; true, otherwise. - */ -bool writeRecord(Context context, Table* table, SQLValue** values, int32 recPos, Heap heap); - -/** - * Writes a record from an array of values in a result set. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param values The record to be written in the result set table. - * @return false if an error occurs; true, otherwise. - */ -bool writeRSRecord(Context context, Table* table, SQLValue** values); - -/** - * Checks if a primary key constraint was violated - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param values The values inserted in the table. - * @param recPos The position of vals record. - * @param newRecord Indicates if it is an inserted or an updated record. - * @param heap A heap to allocate values read from the table. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If a member of the primary key is null. - * @throws PrimaryKeyViolation If a there is a repeated primary key. - */ -bool checkPrimaryKey(Context context, Table* table, SQLValue** values, int32 recPos, bool newRecord, Heap heap); - -/** - * Verifies the null and default values of a statement. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param record The record to be inserted or updated. - * @param statementType The type of the statement, which can be SQLElement.STMT_INSERT or SQLElement.CMD_UPDATE. - * @param nValues The number of values being verified. - * @return false if a null violation occurs; true, otherwise. - * @throws DriverException If a primary key is or a NOT NULL field is is null. - */ -bool verifyNullValues(Context context, Table* table, SQLValue** record, int32 statementType, int32 nValues); - -/** - * Converts the strings of the record into the real values, accordingly to the given table column types. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param record The record whose strings are to be transformed in their real types. - * @param nValues The number of values in the record. - * @return false if an error occurs; true, otherwise. - * @throws SQLParseException If a conversion from string to a number or date/datetime fails. - * @throws DriverException If a blob is passed in a statement that is not prepared. - */ -bool convertStringsToValues(Context context, Table* table, SQLValue** record, uint32 nValues); - -/** - * Updates the CRC32 value with the values of the given buffer. - * - * @param buffer The buffer. - * @param length The number of bytes to be used to update the CRC code. - * @param oldCRC The previous CRC32 value. - * @return The CRC32 code updated to include the buffer data. - */ -int32 updateCRC32(uint8* buffer, int32 length, int32 oldCRC); - -/** - * Makes the table for a fast CRC. - */ -void make_crc_table(void); - -/** - * Resets the auxiliary rowid. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @return false if an error occurs; true, otherwise. - */ -bool resetAuxRowId(Context context, Table* table); - -/** - * Changes the state of a row to updated. - * - * @param id The rowid to have its atribute changed. - * @return The rowid with its atribute changed to updated. - */ -int32 rowUpdated(int32 id); - -/** - * Frees a table when closing a Litebase connection. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @return false if an error occurs; true, otherwise. - */ -bool freeTableHT(Context context, Table* table); - -/** - * Closes a table. - * - * @param context The thread context where the function is being executed. - * @param table The table. - * @param isDelete Indicates if the table is to be deleted. - * @param updatePos Indicates if the .db file is to be truncated or not. - * @return false if an error occurs; true, otherwise. - */ -bool freeTable(Context context, Table* table, bool isDelete, bool updatePos); - -/** - * Gets the value of a column of the underlying table used by the result set. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set whose table will be read. - * @param column The number of the column from which the value will be fetched. - * @param value The structure where the value will be stored. - * @return false if an error occurs; true, otherwise. - */ -bool getTableColValue(Context context, ResultSet* resultSet, int32 column, SQLValue* value); - -/** - * Indicates if a table already exists on disk or not. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param name The table name. - * @return true if the table already exists or the table name is too big; false, othewise. - * @throws AlreadyCreatedException If the table is already created. - * @throws DriverException If the path is too long. - */ -bool tableExistsByName(Context context, TCObject driver, CharP name); - -/** - * Gets the table name on disk. - * - * @param context The thread context where the function is being executed. - * @param crid The application id for the connection. - * @param name The table name. - * @param buffer The buffer for the table name on disk. - * @return false if table name is too big; true, otherwise. - * @throws DriverException If the table name is too big. - */ -bool getDiskTableName(Context context, int32 crid, CharP name, CharP buffer); - -/** - * Given a table name as an object, returns its table. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param name The table name as a string object. - * @return null if an error occurs; a table handle, otherwise. - * @throws DriverException If the table name is too big. - */ -Table* getTableFromName(Context context, TCObject driver, TCObject name); - -/** - * Given a table name as an uint8 string, returns its table. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param tableName The table name as an uint8 string. - * @return null if an error occurs; a table handle, otherwise. - * @throws DriverException If the table name is too big. - * @throws OutOfMemoryError If there is not enougth memory to be allocated - */ -Table* getTable(Context context, TCObject driver, CharP tableName); - -/** - * Reads a string from a buffer. - * - * @param buffer The buffer being read. - * @param string The string returned. - * @param heap The heap where the string will be allocated. - * @return The buffer remaning after reading the string. - */ -uint8* readString(uint8* buffer, CharP* string, Heap heap); - -/** - * Reads a string array from a buffer. - * - * @param buffer The buffer being read. - * @param strings The string array returned. - * @param count The column count. - * @param heap The heap where the string array will be allocated. - * @return The buffer remaning after reading the string array. - */ -uint8* readStringArray(uint8* buffer, CharP** strings, int32 count, Heap heap); - -/** - * Writes a string to a buffer. - * - * @param buffer The buffer being written. - * @param string The string to be written. - * @return The buffer remaning after writing the string. - */ -uint8* writeString(uint8* buffer, CharP string); - -/** - * Writes a string array to a buffer. - * - * @param buffer The buffer being written. - * @param strings The string array to be written. - * @param count The column count. - * @return The buffer remaning after writing the string array. - */ -uint8* writeStringArray(uint8* buffer, CharP* strings, int32 count); - -/** - * Writes a unicode string to a buffer. - * - * @param buffer The buffer being written. - * @param string The string to be written. - * @param lengtg The string length. - * @return The buffer remaning after writing the string. - */ -uint8* writeString16(uint8* buffer, JCharP string, int32 length); - -/** - * Changes a table to the modified state whenever it is modified. - * - * @param context The thread context where the function is being executed. - * @param table The table to be set as modified. - * @return false if an error occurs; true, otherwise. - */ -bool setModified(Context context, Table* table); - -/** - * Rands between two numbers. - * - * @param low The first and smaller number. - * @param high The second and greater number. - * @return a pseudo-random number between them. - */ -int32 randBetween(int32 low, int32 high); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if rowUpdated() works correctly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_rowUpdated(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/UtilsLB.c b/LitebaseSDK/src/native/UtilsLB.c deleted file mode 100644 index 38fb8e7a12..0000000000 --- a/LitebaseSDK/src/native/UtilsLB.c +++ /dev/null @@ -1,1041 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This module defines useful functions for other Litebase modules. - */ - -#include "UtilsLB.h" - -/** - * Compares 2 unicode strings, similar to xstrcmp(). - * - * @param string1 The first string to be compared. - * @param string2 The second string to be compared. - * @param length1 The length of the first string. - * @param length2 The length of the second string. - * @param isCaseless Indicates if the comparison is caseless or not. - * @return 0 if both strings are equal; a positive value if first character that does not match has a greater value in string1 than in string2; a - * negative value, otherwise. - */ -int32 str16CompareTo(JCharP string1, JCharP string2, int32 length1, int32 length2, bool isCaseless) -{ - TRACE("str16CompareTo") - uint32 n = (length1 < length2)? length1 : length2; - - if (isCaseless) - { - JChar char1, - char2; - while (n--) - { - if ((char1 = TC_JCharToLower(*string1)) == (char2 = TC_JCharToLower(*string2))) - { - string1++; - string2++; - } - else return (int32)char1 - (int32)char2; - } - } - else - { - while (n--) - { - if (*string1 == *string2) - { - string1++; - string2++; - } - else return (int32)*string1 - (int32)*string2; - } - } - - return length1 - length2; -} - -/** - * Compares 2 unicode strings to see one string is the prefix of another string. - * - * @param charsStr The string where the prefix will be searched. - * @param prefixStr The prefix to search in charsStr. - * @param charsLen The length of charsStr. - * @param prefixLen The length of prefixStr. - * @param srcOffset The offset on charsStr where to start searching. - * @param isCaseless Indicates if the comparison is caseless or not. - * @return true if prefixStr is a prefix of charsStr with the given offset; false, otherwise. - */ -bool str16StartsWith(JCharP charsStr, JCharP prefixStr, int32 charsLen, int32 prefixLen, int32 srcOffset, bool isCaseless) -{ - TRACE("str16StartsWith") - int32 n = prefixLen; - if (!n || srcOffset < 0 || srcOffset > charsLen - n) - return false; - charsStr += srcOffset; - if (isCaseless) - while (--n >= 0) - { - if (TC_JCharToLower(*charsStr++) != TC_JCharToLower(*prefixStr++)) - return false; - } - else - while (--n >= 0) - if (*charsStr++ != *prefixStr++) - return false; - return true; -} - -/** - * Returns the index where a substring beggins in another string. - * - * @param charsStr The string where to find the substring. - * @param subStr The substring to be found in the first string. - * @param charsLen The length of charsStr. - * @param subLen The length of subStr. - * @param isCaseless Indicates if the search is caseless or not. - * @return 0 if the substring is empty; the index of the substring in the first string, or -1 if the substring does not occur in the other string. - */ -int32 str16IndexOf(JCharP charsStr, JCharP subStr, int32 charsLen, int32 subLen, bool isCaseless) -{ - TRACE("str16IndexOf") - JChar c; - int32 j = (charsLen - subLen), - i = 0; - uint32 len; - bool found = false; - JCharP string1, - string2; - - if (!subLen--) - return 0; - - if (isCaseless) - { - c = TC_JCharToLower(*subStr++); - while (!found) - { - while (i <= j && TC_JCharToLower(*charsStr) != c) // Searches for the next ocurrence of the first char. - { - i++; - charsStr++; - } - if (i > j) // Has passed the end of the string? - return -1; - - // Now searches the rest of the string. - len = subLen; - string1 = charsStr + 1; - string2 = subStr; - if (!len) // juliana@223_7: corrected a bug on like contains ('%...%'). - found = true; - while (len--) - { - if (TC_JCharToLower(*string1++) != TC_JCharToLower(*string2++)) - { - i++; - charsStr++; // guich@321_3 - found = false; // juliana@223_7: corrected a bug on like contains ('%...%'). - break; - } - found = true; - } - } - } - else - { - c = *subStr++; - while (!found) - { - while (i <= j && *charsStr != c) // Searches for the next ocurrence of the first char. - { - i++; - charsStr++; - } - if (i > j) // Has passed the end of the string? - return -1; - - // Now searches the rest of the string. - len = subLen; - string1 = charsStr + 1; - string2 = subStr; - if (!len) // juliana@223_7: corrected a bug on like contains ('%...%'). - found = true; - while (len--) - { - if (*string1++ != *string2++) - { - i++; - charsStr++; // guich@321_3 - found = false; // juliana@223_7: corrected a bug on like contains ('%...%'). - break; - } - found = true; - } - } - } - return i; -} - -/** - * Returns the number of days that a specific month has. - * - * @param month The month number: from 1 to 12. - * @param year The year. - * @return The number of days that the month has, taking a possible leap year into consideration. - */ -int32 getDaysInMonth(int32 month, int32 year) // rnovais@567_2 -{ - TRACE("getDaysInMonth") - if (month != 2) - return monthDays[month - 1]; - return (!(year % 4) && ((year % 100) || !(year % 400)))? 29 : 28; -} - -/** - * Verifies if year, month, and day forms a valid date. - * - * @param year The year. - * @param month The month. - * @param day The day. - * @return An int of the format YYYYMMDD if the date is valid; otherwise, returns -1. -*/ -int32 verifyDate(int32 year, int32 month, int32 day) // rnovais@567_2 -{ - TRACE("verifyDate") - if (100 <= year && year <= 999) // year contains 3 digits. - return -1; - if (0 <= year && year < 20) // From 2000 to 2019 - year += 2000; - else - if (20 <= year && year < 100) // From 1920 to 1999 - year += 1900; - - // Checks if the day and month values are valid, if the number of days in the month is valid and if the year is not too big. - if (day > 0 && month >= 1 && month <= 12 && day <= getDaysInMonth(month, year) && year < 3000) - return (year * 10000) + (month * 100) + day; - else - return -1; -} - -/** - * Does a left trim in a string. - * - * @param chars The string to be trimed. - * @return The string with the blanks in the beggining trimmed. - */ -CharP strLeftTrim(CharP chars) -{ - TRACE("strLeftTrim") - while (*chars == ' ') - chars++; - return chars; -} - -/** - * Does a left and right trim in a string. - * - * @param chars The string to be trimmed. - * @return The string with the blanks in the beggining and in the end trimmed. - */ -CharP strTrim(CharP chars) -{ - TRACE("strTrim") - int32 j; - - while (*chars == ' ') // Left trim. - chars++; - if ((j = xstrlen(chars) - 1) < 0) - return chars; - while (chars[j] == ' ') // Right trim. - j--; - chars[j + 1] = 0; // Zeroes the end of the string. - return chars; -} - -/** -* Does a left and right trim in tchar a string. -* -* @param chars The tchar string to be trimmed. -* @return The tchar string with the blanks in the beggining and in the end trimmed. -*/ -TCHARP tstrTrim(TCHARP chars) -{ - TRACE("tstrTrim") - int32 j; - - while (*chars == ' ') // Left trim. - chars++; - if ((j = tcslen(chars) - 1) < 0) - return chars; - while (chars[j] == ' ') // Right trim. - j--; - chars[j + 1] = 0; // Zeroes the end of the string. - return chars; -} - -/** - * Does a left trim in a unicode string. - * - * @param string16Str The string to be trimmed. - * @param string16Len The length of the string to be trimmed, which is updated to return the length of the string trimmed. - * @return The string with blanks in the beggining. - */ -JCharP str16LeftTrim(JCharP string16Str, int32* string16Len) -{ - TRACE("str16LeftTrim") - while (*string16Str == ' ') // Left trim. - { - string16Str++; - (*string16Len)--; - } - return string16Str; -} - -/** - * Verifies if a string is a valid Date and transforms it into a correspondent int date. - * - * @param chars A string in a date format. - * @returns A correspondent int datetime or -1 if the date is invalid. - */ -int32 testAndPrepareDate(CharP chars) -{ - TRACE("testAndPrepareDate") - int32 i = -1, - j = 0, - start = 0, - len = 0, - n = xstrlen(chars), - pos; - int32 p[3]; - bool err; - char c; - if (5 <= n && n <= 10) // 1/1/1 to DD/MM/YYYY - { - while (++i < n) - { - c = chars[i]; - if ('0' <= c && c <= '9') - len++; - else if (j == 2) // If there's already two separators, error! - break; - else - { - chars[pos = start + len] = 0; - p[j++] = TC_str2int(&chars[start], &err); - if (err) - return -1; - start += len + 1; - len = 0; - chars[pos] = c; // Returns the original character. - } - } - if (j == 2) // Has it found exactly 2 separators? - { - p[2] = TC_str2int(&chars[start], &err); - if (err) - return -1; - return verifyDate(p[0], p[1], p[2]); - } - } - return -1; -} - -/** - * Verifies if a string is a valid Time and transforms it into a correspondent int datetime. The time ranges from 00:00:00:000 to 23:59:59:9999 (it - * accepts dots and colons). This method is very flexible. For instance: 2:-:8:10 is the same as 2:0:8:10 and returns 20008010; 02:.:8:1 is the same - * as 02:0.0:8 and returns 20000008; :4:8:19 is the same as 0:4:8:19 and returns; 408019 2.4.a.876 is the same as 2.4.0.876 and returns 20400876. - * - * @param chars A string in a time format. - * @returns A correspondent int datetime or -1 if the value is not a valid time. - */ -int32 testAndPrepareTime(CharP chars) -{ - TRACE("testAndPrepareTime") - int32 i = -1, - j = 0, - start = 0, - len = 0, - n = xstrlen(chars), - hour, - minutes, - seconds, - millis, - pos; - int32 p[4]; - char c; - bool err; - - p[0] = p[1] = p[2] = p[3] = 0; - if (n > 0 && n <= 13) - { - while (++i < n) - { - c = chars[i]; - if ('0' <= c && c <= '9') - len++; - else if (j == 3) // If there's already three separators, error! - break; - else - { - chars[pos = start + len] = 0; - p[j++] = TC_str2int(&chars[start], &err); - if (err) - return -1; - start += len + 1; - len = 0; - chars[pos] = c; - } - } - p[j++] = TC_str2int(&chars[start], &err); - if (err) - return -1; - - if ((hour = p[0]) < 0 || hour > 23 || (minutes = p[1]) < 0 || minutes > 59 || (seconds = p[2]) < 0 || seconds > 59 || (millis = p[3]) < 0 || millis > 999) - return -1; - return hour * 10000000 + minutes * 100000 + seconds * 1000 + millis; - } - else - return -1; -} - -/** - * Verifies if a string is a valid date or datetime and transforms it into a corresponding date or datetime. - * - * @param context The thread context where the function is being executed. - * @param value The record value which will hold the date or datetime as integer(s). - * @param chars The date or datetime as a string. - * @param type DATE_TYPE or DATETIME_TYPE. - * @return false if the string format is wrong; true, otherwise. - * @throws SQLParseException If the string format is wrong. - */ -bool testAndPrepareDateAndTime(Context context, SQLValue* value, CharP chars, int32 type) -{ - TRACE("testAndPrepareDateAndTime") - CharP str = strTrim(chars); - - if (type == DATE_TYPE && (value->asInt = testAndPrepareDate(str)) == -1) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_VALUE_ISNOT_DATE), chars); - return false; - } - else if (type == DATETIME_TYPE) - { - CharP posSpace = xstrchr(str, ' '); - if (posSpace) - { - *posSpace = 0; - value->asDate = testAndPrepareDate(strTrim(str)); // Gets the date part. - value->asTime = testAndPrepareTime(strTrim(posSpace + 1)); // Gets the time part. - } - else - { - value->asInt = testAndPrepareDate(str); - value->asTime = 0; // The time part is 0. - } - - if ((value->asDate == -1) || (value->asTime == -1)) - { - if (posSpace) - *posSpace = ' '; - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_VALUE_ISNOT_DATETIME), chars); - return false; - } - } - return true; -} - -/** - * Creates an IntVector with the given initial capacity. - * - * @param count The IntVector initial capacity. - * @param heap A heap to allocate the IntVector. - * @return The new int vector created. - */ -IntVector newIntVector(int32 count, Heap heap) -{ - TRACE("newIntVector") - IntVector iv; - - iv.length = count; - iv.size = 0; - iv.items = (int32*)TC_heapAlloc(iv.heap = heap, count << 2); // Allocates in the heap. - return iv; -} - -/** - * Adds an integer to the IntVector, enlarging it if necessary. - * - * @param intVector The IntVector. - * @param value The integer value to be inserted in the IntVector. - */ -void IntVectorAdd(IntVector* intVector, int32 value) -{ - TRACE("IntVectorAdd") - if (intVector->size == intVector->length) - { - int32 length = intVector->length; - int32* items = (int32*)TC_heapAlloc(intVector->heap, (length + 1) << 3); // Allocates in the heap. - - xmemmove(items, intVector->items, length << 2); - intVector->items = items; - intVector->length <<= 1; - } - intVector->items[intVector->size++] = value; -} - -/** - * Duplicates an int array when is necessary to create a copy of it. - * - * @param intArray The int array to be duplicated. - * @param size The size of the array. - * @param heap The heap to allocate the array. - * @return The duplicated int array. - */ -int32* duplicateIntArray(int32* intArray, int32 size, Heap heap) -{ - TRACE("shortVector2Array") - int32* newArray = (int32*)TC_heapAlloc(heap, size << 2); - xmemmove(newArray, intArray, size << 2); - return newArray; -} - -/** - * Duplicates a byte array when is necessary to create a copy of it. - * - * @param byteArray The byte array to be duplicated. - * @param size The size of the array. - * @param heap The heap to allocate the array. - * @return The duplicated byte array. - */ -int8* duplicateByteArray(int8* byteArray, int32 size, Heap heap) -{ - TRACE("shortVector2Array") - int8* newArray = (int8*)TC_heapAlloc(heap, size); - xmemmove(newArray, byteArray, size); - return newArray; -} - -/** - * Creates an empty full IntVector. - * - * @param count The size of the IntVector, which can't be null. - * @param heap A heap to allocate the IntVector integer array. - * @return The IntVector. - */ -IntVector newIntBits(int32 count, Heap heap) -{ - TRACE("newIntBits") - IntVector intVector; - intVector = newIntVector(count = (count >> 5) + 1, heap); - intVector.size = count; - return intVector; -} - -/** - * Finds the next bit set from an b-tree. - * - * @param intVector The IntVector with the index bitmap. - * @param start The first value to search. - * @return The position of the next bit set. - */ -int32 findNextBitSet(IntVector* intVector, int32 start) -{ - TRACE("findNextBitSet") - int32 index = start >> 5, // Converts from bits to int. - n; - uint32 b; - start &= 31; - while (1) - { - if ((n = intVector->size - index) > 0 && !intVector->items[index]) // guich@104 - { - start = 0; // guich@104 - while (n > 0 && !intVector->items[index]) // Finds the next int with any bit set. - { - n--; - index++; - } - } - if (n > 0) // Found? - { - b = intVector->items[index]; - while (start < 32 && !(b & ((int32)1 << start))) - start++; - if (start == 32) - { - start = 0; - index++; // No more bits in this int? Tests next ints. - continue; - } - return start + (index << 5); - } - return -1; - } -} - -/** - * Compares the two records, using the sort column list. - * - * @param record1 The first record to be compared. - * @param record2 The second record to be compared. - * @param nullsRecord1 The null values of the first record. - * @param nullsRecord2 The null values of the second record. - * @param sortFieldListCount The number of elements of sortFieldList. - * @param sortFieldList The order of evaluation of the record. - * @return 0 if the arrays are identical in the comparison order; a positive number if record1[] is greater than record2[]; - * otherwise, a negative number. - */ -int32 compareRecords(SQLValue** record1, SQLValue** record2, uint8* nullsRecord1, uint8* nullsRecord2, int32 sortFieldListCount, - SQLResultSetField** sortFieldList) -{ - TRACE("compareRecords") - int32 i = -1, - index, - result; - SQLResultSetField* field; - - while (++i < sortFieldListCount) // Compares the records, using the sequence used by the sort column List. - { - index = (field = sortFieldList[i])->tableColIndex; - - // Compares the elements checking if they are null. - result = valueCompareTo(null, record1[index], record2[index], field->dataType, isBitSet(nullsRecord1, index), isBitSet(nullsRecord2, index), null); - - if (!field->isAscending) - result = -result; - - if (result) - return result; - } - - return 0; -} - -/** - * Sets and resets one bit in an array of bytes. - * - * @param items The array of bytes - * @param index The bit index to be set or reset. - * @param isOn A bool that defines whether the bit will be set or reset. - */ -void setBit(uint8* items, int32 index, bool isOn) -{ - TRACE("setBit") - if (isOn) - setBitOn(items, index); // Sets - else - setBitOff(items, index); // Resets. -} - -/** - * Gets the full name of a file: path + file name. - * - * @param fileName The file name. - * @param sourcePath The path where the table is stored. - * @param buffer Receives path + file name with a path separator if necessary. - */ -void getFullFileName(CharP fileName, TCHARP sourcePath, TCHARP buffer) -{ - TRACE("getFullFileName") - int32 endChar = tcslen(sourcePath) - 1; - - // juliana@223_6: Corrected a bug that would create spourious paths if they had a stress on Windows CE. - tcscpy(buffer, sourcePath); - if (sourcePath[endChar] != PATH_SEPARATOR && sourcePath[endChar] != NO_PATH_SEPARATOR) - tcscat(buffer, TEXT("/")); - TC_CharP2TCHARPBuf(fileName, &buffer[tcslen(buffer)]); -} - -/** - * Returns the time in the format YYYYMMDDHHMMSS as a long value. It does not include the millis. - * - * @param year The year. - * @param month The month. - * @param day The day. - * @param hour The hour. - * @param minute The minute. - * @param second The second. - * @return The time in the format YYYYMMDDHHMMSS. - */ -int64 getTimeLong(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second) -{ - TRACE("getTimeLong") - return (int64)year * (int64)1000000000L * (int64)10L + month * 100000000L + day * 1000000 + hour * 10000 + minute * 100 + second; -} - -/** - * Checks if a unicode string starts with a substring in the ascii format. - * - * @param unicodeStr The unicode string. - * @param asciiStr The ascii string. - * @param unicodeLen The unicode string length. - * @param asciiLen The ascii string length. - * @return true if the unicode string starts with the ascii string; false, otherwise. - */ -bool JCharPStartsWithCharP(JCharP unicodeStr, CharP asciiStr, int32 unicodeLen, int32 asciiLen) -{ - TRACE("JCharPStartsWithCharP") - - if (asciiLen > unicodeLen) // If the substring is greater than the string, the result i false. - return false; - - else // Checks if each asciiStr character equals each unicodeStr character. - while (asciiLen-- > 0) - if (*unicodeStr++ != *asciiStr++) - return false; - return true; -} - -/** - * Checks if a unicode string is equal to the ascii format. - * - * @param unicodeStr The unicode string. - * @param asciiStr The ascii string. - * @param unicodeLen The unicode string length. - * @param asciiLen The ascii string length. - * @param ignoreCase Indicates if the case is to be taken into consideration or not. - * @return true if the unicode is equal to the ascii string; false, otherwise. - */ -bool JCharPEqualsCharP(JCharP unicodeStr, CharP asciiStr, int32 unicodeLen, int32 asciiLen, bool ignoreCase) -{ - TRACE("JCharPEqualsCharP") - - if (asciiLen > unicodeLen) // If the substring is greater than the string, the result i false. - return false; - - if (ignoreCase) // caseless - { - while (asciiLen-- > 0) - if (TC_JCharToLower(*unicodeStr++) != TC_JCharToLower(*asciiStr++)) - return false; - } - else // case sensitive - while (asciiLen-- > 0) - if (*unicodeStr++ != *asciiStr++) - return false; - return true; -} - -// juliana@230_4 -/** - * Gets the current path used by the system to store application files. - * - * @param sourcePath The path used by the system to store application files. - */ -void getCurrentPath(TCHARP sourcePath) -{ - TRACE("getCurrentPath") - char buffer[MAX_PATHNAME]; - - if (!TC_getDataPath(buffer) || buffer[0] == 0) - xstrcpy(buffer, TC_getAppPath()); - TC_CharP2TCHARPBuf(buffer, sourcePath); -} - -/** - * Formats a date in a unicode buffer. - * - * @param year Year. - * @param month Month. - * @param day Day. - * @param buffer The buffer for the unicode formated date. - */ -void date2JCharP(int32 year, int32 month, int32 day, JCharP buffer) -{ - TRACE("date2JCharP") - DateBuf dateTimeBuf; - - xstrprintf(dateTimeBuf, "%04d/%02d/%02d", year, month, day); - TC_CharP2JCharPBuf(dateTimeBuf, 10, buffer, false); // juliana@238_1: corrected the end quote not appearing in the log files after dates. -} - -/** - * Formats a date time in a unicode buffer. - * - * @param year Year. - * @param month Month. - * @param day Day. - * @param hour Hour. - * @param minute Minute. - * @param second Second. - * @param millis Millis. - * @param buffer The buffer for the unicode formated date. - */ -void dateTime2JCharP(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second, int32 millis, JCharP buffer) -{ - TRACE("dateTime2JCharP") - DateTimeBuf dateTimeBuf; - - xstrprintf(dateTimeBuf, "%04d/%02d/%02d", year, month, day); - xstrprintf(&dateTimeBuf[11], "%02d:%02d:%02d:%03d", hour, minute, second, millis); - dateTimeBuf[10] = ' '; - TC_CharP2JCharPBuf(dateTimeBuf, 23, buffer, false); -} - -/** - * Converts a short stored in a string into a short. - * - * @param chars The string storing a short. - * @param error Receives true if an error occured during the conversion; false, otherwise. - * @return The short if the convertion succeeds. - */ -int32 str2short(CharP chars, bool* error) -{ - TRACE("str2short") - int32 value = TC_str2int(chars, error); - - // juliana@227_18: corrected a possible insertion of a negative short column being recovered in the select as positive. - // juliana@225_15: when using short values, if it is out of range an exception must be thrown. - if (value < MIN_SHORT_VALUE || value > MAX_SHORT_VALUE) - *error = true; - - return value; -} - -/** - * Converts a float stored in a string into a float. - * - * @param chars The string storing a float. - * @param error Receives true if an error occured during the conversion; false, otherwise. - * @return The float if the convertion succeeds. - */ -float str2float(CharP chars, bool* error) -{ - TRACE("str2float") - float value = (float)TC_str2double(chars, error); - - if ((value = (value < 0)? - value : value) && (value < MIN_FLOAT_VALUE || value > MAX_FLOAT_VALUE)) - *error = true; - - return value; -} - -/** - * Creates and sets a date object fields using a date stored in a int. - * - * @param p->retO receives The date object to be set. - * @param date The date as an int in the format YYYYMMAA. - * @return false if an error occurs; true, otherwise. - */ -bool setDateObject(NMParams params, int32 date) -{ - TCObject object = params->retO = TC_createObject(params->currentContext, "totalcross.util.Date"); - - if (object) - { - TC_setObjectLock(object, UNLOCKED); - - // Sets the Date object. - FIELD_I32(object, 0) = date % 100; - FIELD_I32(object, 1) = (date /= 100) % 100; - FIELD_I32(object, 2) = date / 100; - - return true; - } - return false; -} - -/** - * Creates and sets a time object fields using a date and a time stored in two ints. - * - * @param p->retO Receives the time object to be set. - * @param date The date stored into a int in the format YYYYMMAA. - * @param time The time stored into a int in the format HHMMSSmmm. - * @return false if an error occurs; true, otherwise. - */ -bool setTimeObject(NMParams params, int32 date, int32 time) -{ - TCObject object = params->retO = TC_createObject(params->currentContext, "totalcross.sys.Time"); - - if (object) - { - TC_setObjectLock(object, UNLOCKED); - - // Sets the date part of the Time object. - Time_day(object) = date % 100; - Time_month(object) = (date /= 100) % 100; - Time_year(object) = date / 100; - - // Sets the time part of the Time object. - Time_millis(object) = time % 1000; - Time_second(object) = (time /= 1000) % 100; - Time_minute(object) = (time /= 100) % 100; - Time_hour(object) = (time / 100) % 100; - - return true; - } - return false; -} - -/** - * Creates a new hash table for the temporary tables size statistics. - * - * @param count The initial size. - * @return A hash table for the temporary tables size statistics. - */ -MemoryUsageHT muNew(int32 count) -{ - MemoryUsageHT table; - - table.size = 0; - table.items = (MemoryUsageEntry**)xmalloc(count * TSIZE); - table.hash = (table.threshold = count) - 1; - - return table; -} - -/** - * Gets the stored statistics item with the given key. - * - * @param table A hash table for the temporary tables size statistics. - * @param key The hash key. - * @param dbSize Receives the stored .db file size. - * @param dboSize Receives the stored .dbo file size. - * @return true if there are statistics stored for the given select hash code; false, otherwise. - */ -bool muGet(MemoryUsageHT* table, int32 key, int32* dbSize, int32* dboSize) -{ - if (table->items && table->size > 0) // guich@tc113_14: check size - { - int32 index = key & table->hash; - MemoryUsageEntry* entry = table->items[index]; - - while (entry) - { - if (entry->key == key) - { - *dbSize = entry->dbSize; - *dboSize = entry->dboSize; - return true; - } - entry = entry->next; - } - } - return false; -} - -/** - * Once the number of elements gets above the load factor, rehashes the hash table. - * - * @param table A hash table for the temporary tables size statistics. - * @return true if there is enough memory to rehashes the table; false, otherwise. - */ -bool muRehash(MemoryUsageHT* table) -{ - int32 oldCapacity = table->hash + 1, - i = oldCapacity, - index, - newCapacity = oldCapacity << 1; - MemoryUsageEntry** oldTable = table->items; - MemoryUsageEntry** newTable = (MemoryUsageEntry **)xmalloc(TSIZE * newCapacity); - MemoryUsageEntry* entry; - MemoryUsageEntry* old; - - if (!newTable) - return false; - - table->threshold = newCapacity * 75 / 100; - table->items = newTable; - table->hash = newCapacity - 1; - - while (i-- > 0) - { - old = oldTable[i]; - while ((entry = old)) - { - old = old->next; - entry->next = newTable[index = entry->key & table->hash]; - newTable[index] = entry; - } - } - xfree(oldTable); - - return true; -} - -/** - * Puts the given pair of key/values in the hash table. If the key already exists, the value will be replaced. - * - * @param table A hash table for the temporary tables size statistics. - * @param key The hash key. - * @param dbSize The .db file size to be stored. - * @param dboSize The .dbo file size to be stored. - * @return true if its is not possible to store a new element; false, otherwise. - */ -bool muPut(MemoryUsageHT* table, int32 key, int32 dbSize, int32 dboSize) -{ - int32 index = key & table->hash; - MemoryUsageEntry* entry = table->items[index]; - if (table->size > 0) // Only searchs in non-empty hash tables. - { - while (entry) // Makes sure the key is not already in the hashtable. - { - if (entry->key == key) - { - entry->dbSize = dbSize; - entry->dboSize = dboSize; - return true; - } - entry = entry->next; - } - } - if (table->size >= table->threshold) // Rehashs the table if the threshold is exceeded. - { - muRehash(table); - index = key & table->hash; - } - - if (!(entry = (MemoryUsageEntry*)xmalloc(sizeof(MemoryUsageEntry)))) // Creates the new entry. - return false; - entry->key = key; - entry->dbSize = dbSize; - entry->dboSize = dboSize; - entry->next = table->items[index]; - table->items[index] = entry; - table->size++; - return true; -} - -/** - * Frees the hashtable. - * - * @param iht A hash table for the temporary tables size statistics. - */ -void muFree(MemoryUsageHT* table) -{ - MemoryUsageEntry** tab = table->items; - MemoryUsageEntry* entry; - MemoryUsageEntry* next; - int32 n = table->hash; - - if (!tab) - return; - while (n-- >= 0) - { - entry = *tab++; - while (entry) - { - next = entry->next; - xfree(entry); - entry = next; - } - } - xfree(table->items); - table->size = 0; -} - -/** - * Indicates if a buffer is only composed by zeros or not. - * - * @param buffer The buffer. - * @param length The size of the buffer. - * @return true if the buffer is only composed by zeros; false, otherwise. - */ -bool isZero(uint8* buffer, int32 length) -{ - while (--length >= 0) - if (buffer[length]) - return false; - return true; -} diff --git a/LitebaseSDK/src/native/UtilsLB.h b/LitebaseSDK/src/native/UtilsLB.h deleted file mode 100644 index 3f8c79216c..0000000000 --- a/LitebaseSDK/src/native/UtilsLB.h +++ /dev/null @@ -1,381 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This module declares useful functions for other Litebase modules. - */ - -#ifndef LITEBASE_UTILSLB_H -#define LITEBASE_UTILSLB_H - -#include "Litebase.h" - -/** - * Compares 2 unicode strings, similar to xstrcmp(). - * - * @param string1 The first string to be compared. - * @param string2 The second string to be compared. - * @param length1 The length of the first string. - * @param length2 The length of the second string. - * @param isCaseless Indicates if the comparison is caseless or not. - * @return 0 if both strings are equal; a positive value if first character that does not match has a greater value in string1 than in string2; a - * negative value, otherwise. - */ -int32 str16CompareTo(JCharP string1, JCharP string2, int32 length1, int32 length2, bool isCaseless); - -/** - * Compares 2 unicode strings to see one string is the prefix of another string. - * - * @param charsStr The string where the prefix will be searched. - * @param prefixStr The prefix to search in charsStr. - * @param charsLen The length of charsStr. - * @param prefixLen The length of prefixStr. - * @param srcOffset The offset on charsStr where to start searching. - * @param isCaseless Indicates if the comparison is caseless or not. - * @return true if prefixStr is a prefix of charsStr with the given offset; false, otherwise. - */ -bool str16StartsWith(JCharP charsStr, JCharP prefixStr, int32 charsLen, int32 prefixLen, int32 srcOffset, bool isCaseless); - -/** - * Returns the index where a substring beggins in another string. - * - * @param charsStr The string where to find the substring. - * @param subStr The substring to be found in the first string. - * @param charsLen The length of charsStr. - * @param subLen The length of subStr. - * @param isCaseless Indicates if the search is caseless or not. - * @return 0 if the substring is empty; the index of the substring in the first string, or -1 if the substring does not occur in the other string. - */ -int32 str16IndexOf(JCharP charsStr, JCharP subStr, int32 charsLen, int32 subLen, bool isCaseless); - -/** - * Returns the number of days that a specific month has. - * - * @param month The month number: from 1 to 12. - * @param year The year. - * @return The number of days that the month has, taking a possible leap year into consideration. - */ -int32 getDaysInMonth(int32 month, int32 year); - -/** - * Verifies if year, month, and day forms a valid date. - * - * @param year The year. - * @param month The month. - * @param day The day. - * @return An int of the format YYYYMMDD if the date is valid; otherwise, returns -1. -*/ -int32 verifyDate(int32 year, int32 month, int32 day); - -/** - * Does a left trim in a string. - * - * @param chars The string to be trimed. - * @return The string with the blanks in the beggining trimmed. - */ -CharP strLeftTrim(CharP chars); - -/** - * Does a left and right trim in a string. - * - * @param chars The string to be trimmed. - * @return The string with the blanks in the beggining and in the end trimmed. - */ -CharP strTrim(CharP chars); - -/** -* Does a left and right trim in tchar a string. -* -* @param chars The tchar string to be trimmed. -* @return The tchar string with the blanks in the beggining and in the end trimmed. -*/ -TCHARP tstrTrim(TCHARP chars); - -/** - * Does a left trim in a unicode string. - * - * @param string16Str The string to be trimmed. - * @param string16Len The length of the string to be trimmed, which is updated to return the length of the string trimmed. - * @return The string with blanks in the beggining. - */ -JCharP str16LeftTrim(JCharP string16Str, int32* string16Len); - -/** - * Verifies if a string is a valid Date and transforms it into a correspondent int date. - * - * @param chars A string in a date format. - * @returns A correspondent int datetime or -1 if the date is invalid. - */ -int32 testAndPrepareDate(CharP chars); - -/** - * Verifies if a string is a valid Time and transforms it into a correspondent int datetime. The time ranges from 00:00:00:000 to 23:59:59:9999 (it - * accepts dots and colons). This method is very flexible. For instance: 2:-:8:10 is the same as 2:0:8:10 and returns 20008010; 02:.:8:1 is the same - * as 02:0.0:8 and returns 20000008; :4:8:19 is the same as 0:4:8:19 and returns; 408019 2.4.a.876 is the same as 2.4.0.876 and returns 20400876. - * - * @param chars A string in a time format. - * @returns A correspondent int datetime or -1 if the value is not a valid time. - */ -int32 testAndPrepareTime(CharP chars); - -/** - * Verifies if a string is a valid date or datetime and transforms it into a corresponding date or datetime. - * - * @param context The thread context where the function is being executed. - * @param value The record value which will hold the date or datetime as integer(s). - * @param chars The date or datetime as a string. - * @param type DATE_TYPE or DATETIME_TYPE. - * @return false if the string format is wrong; true, otherwise. - * @throws SQLParseException If the string format is wrong. - */ -bool testAndPrepareDateAndTime(Context context, SQLValue* value, CharP chars, int32 type); - -/** - * Creates an IntVector with the given initial capacity. - * - * @param count The IntVector initial capacity. - * @param heap A heap to allocate the IntVector. - * @return The new intVector created. - */ -IntVector newIntVector(int32 count, Heap heap); - -/** - * Adds an integer to the IntVector, enlarging it if necessary. - * - * @param intVector The IntVector. - * @param value The integer value to be inserted in the IntVector. - */ -void IntVectorAdd(IntVector* intVector, int32 value); - -/** - * Duplicates an int array when is necessary to create a copy of it. - * - * @param intArray The int array to be duplicated. - * @param size The size of the array. - * @param heap The heap to allocate the array. - * @return The duplicated int array. - */ -int32* duplicateIntArray(int32* intArray, int32 size, Heap heap); - -/** - * Duplicates a byte array when is necessary to create a copy of it. - * - * @param byteArray The byte array to be duplicated. - * @param size The size of the array. - * @param heap The heap to allocate the array. - * @return The duplicated byte array. - */ -int8* duplicateByteArray(int8* byteArray, int32 size, Heap heap); - -/** - * Creates an empty full IntVector. - * - * @param count The size of the IntVector, which can't be null. - * @param heap A heap to allocate the IntVector integer array. - * @return The IntVector. - */ -IntVector newIntBits(int32 count, Heap heap); - -/** - * Finds the next bit set from an b-tree. - * - * @param intVector The IntVector with the index bitmap. - * @param start The first value to search. - * @return The position of the next bit set. - */ -int32 findNextBitSet(IntVector* intVector, int32 start); - -/** - * Compares the two records, using the sort column list. - * - * @param record1 The first record to be compared. - * @param record2 The second record to be compared. - * @param nullsRecord1 The null values of the first record. - * @param nullsRecord2 The null values of the second record. - * @param sortFieldListCount The number of elements of sortFieldList. - * @param sortFieldList The order of evaluation of the record. - * @return 0 if the arrays are identical in the comparison order; a positive number if record1[] is greater than record2[]; - * otherwise, a negative number. - */ -int32 compareRecords(SQLValue** record1, SQLValue** record2, uint8* nullsRecord1, uint8* nullsRecord2, int32 sortFieldListCount, - SQLResultSetField** sortFieldList); - -/** - * Sets and resets one bit in an array of bytes. - * - * @param items The array of bytes - * @param index The bit index to be set or reset. - * @param isOn A bool that defines whether the bit will be set or reset. - */ -void setBit(uint8* items, int32 index, bool isOn); - -/** - * Gets the full name of a file: path + file name. - * - * @param fileName The file name. - * @param sourcePath The path where the table is stored. - * @param buffer Receives path + file name with a path separator if necessary. - */ -void getFullFileName(CharP fileName, TCHARP sourcePath, TCHARP buffer); - -/** - * Returns the time in the format YYYYMMDDHHMMSS as a long value. It does not include the millis. - * - * @param year The year. - * @param month The month. - * @param day The day. - * @param hour The hour. - * @param minute The minute. - * @param second The second. - * @return The time in the format YYYYMMDDHHMMSS. - */ -int64 getTimeLong(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second); - -/** - * Checks if a unicode string starts with a substring in the ascii format. - * - * @param unicodeStr The unicode string. - * @param asciiStr The ascii string. - * @param unicodeLen The unicode string length. - * @param asciiLen The ascii string length. - * @return true if the unicode string starts with the ascii string; false, otherwise. - */ -bool JCharPStartsWithCharP(JCharP unicodeStr, CharP asciiStr, int32 unicodeLen, int32 asciiLen); - -/** - * Checks if a unicode string is equal to the ascii format. - * - * @param unicodeStr The unicode string. - * @param asciiStr The ascii string. - * @param unicodeLen The unicode string length. - * @param asciiLen The ascii string length. - * @param ignoreCase Indicates if the case is to be taken into consideration or not. - * @return true if the unicode is equal to the ascii string; false, otherwise. - */ -bool JCharPEqualsCharP(JCharP unicodeStr, CharP asciiStr, int32 unicodeLen, int32 asciiLen, bool ignoreCase); - -/** - * Gets the current path used by the system to store application files. - * - * @param sourcePath The path used by the system to store application files. - */ -void getCurrentPath(TCHARP sourcePath); - -/** - * Formats a date in a unicode buffer. - * - * @param year Year. - * @param month Month. - * @param day Day. - * @param buffer The buffer for the unicode formated date. - */ -void date2JCharP(int32 year, int32 month, int32 day, JCharP buffer); - -/** - * Formats a date time in a unicode buffer. - * - * @param year Year. - * @param month Month. - * @param day Day. - * @param hour Hour. - * @param minute Minute. - * @param second Second. - * @param millis Millis. - * @param buffer The buffer for the unicode formated date. - */ -void dateTime2JCharP(int32 year, int32 month, int32 day, int32 hour, int32 minute, int32 second, int32 millis, JCharP buffer); - -/** - * Converts a short stored in a string into a short. - * - * @param chars The string storing a short. - * @param error Receives true if an error occured during the conversion; false, otherwise. - * @return The short if the convertion succeeds. - */ -int32 str2short(CharP chars, bool* error); - -/** - * Converts a float stored in a string into a float. - * - * @param chars The string storing a float. - * @param error Receives true if an error occured during the conversion; false, otherwise. - * @return The float if the convertion succeeds. - */ -float str2float(CharP chars, bool* error); - -/** - * Creates and sets a date object fields using a date stored in a int. - * - * @param p->retO receives The date object to be set. - * @param date The date as an int in the format YYYYMMAA. - * @return false if an error occurs; true, otherwise. - */ -bool setDateObject(NMParams params, int32 date); - -/** - * Creates and sets a time object fields using a date and a time stored in two ints. - * - * @param p->retO Receives the time object to be set. - * @param date The date stored into a int in the format YYYYMMAA. - * @param time The time stored into a int in the format HHMMSSmmm. - * @return false if an error occurs; true, otherwise. - */ -bool setTimeObject(NMParams params, int32 date, int32 time); - -/** - * Creates a new hash table for the temporary tables size statistics. - * - * @param count The initial size. - * @return A hash table for the temporary tables size statistics. - */ -MemoryUsageHT muNew(int32 count); - -/** - * Gets the stored statistics item with the given key. - * - * @param table A hash table for the temporary tables size statistics. - * @param key The hash key. - * @param dbSize Receives the stored .db file size. - * @param dboSize Receives the stored .dbo file size. - * @return true if there are statistics stored for the given select hash code; false, otherwise. - */ -bool muGet(MemoryUsageHT* table, int32 key, int32* dbSize, int32* dboSize); - -/** - * Once the number of elements gets above the load factor, rehashes the hash table. - * - * @param table A hash table for the temporary tables size statistics. - * @return true if there is enough memory to rehashes the table; false, otherwise. - */ -bool muRehash(MemoryUsageHT* table); - -/** - * Puts the given pair of key/values in the hash table. If the key already exists, the value will be replaced. - * - * @param table A hash table for the temporary tables size statistics. - * @param key The hash key. - * @param dbSize The .db file size to be stored. - * @param dboSize The .dbo file size to be stored. - * @return true if its is not possible to store a new element; false, otherwise. - */ -bool muPut(MemoryUsageHT* table, int32 key, int32 dbSize, int32 dboSize); - -/** - * Frees the hashtable. - * - * @param table A hash table for the temporary tables size statistics. - */ -void muFree(MemoryUsageHT* table); - -/** - * Indicates if a buffer is only composed by zeros or not. - * - * @param buffer The buffer. - * @param length The size of the buffer. - * @return true if the buffer is only composed by zeros; false, otherwise. - */ -bool isZero(uint8* buffer, int32 length); - -#endif diff --git a/LitebaseSDK/src/native/jni/Android.mk b/LitebaseSDK/src/native/jni/Android.mk deleted file mode 100644 index 510b5906b7..0000000000 --- a/LitebaseSDK/src/native/jni/Android.mk +++ /dev/null @@ -1,73 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -TYPE ?= release - -ifeq ($(TYPE), noras) - include $(LOCAL_PATH)/options_noras.mk -endif -ifeq ($(TYPE), release) - include $(LOCAL_PATH)/options_nodemo.mk -endif -ifeq ($(TYPE), demo) - include $(LOCAL_PATH)/options_demo.mk -endif - -LB_SRCDIR := .. -LB_INCLUDEDIR := $(LB_SRCDIR)/native -TC_SRCDIR := $(LB_SRCDIR)/../../../../TotalCross/TotalCrossVM/src -TC_INCLUDEDIR := $(LB_SRCDIR)/../../../TotalCross/TotalCrossVM/src - -LITEBASE_FILES = \ - $(LB_SRCDIR)/lbFile.c \ - $(LB_SRCDIR)/PlainDB.c \ - $(LB_SRCDIR)/TCVMLib.c \ - $(LB_SRCDIR)/Litebase.c \ - $(LB_SRCDIR)/ResultSet.c \ - $(LB_SRCDIR)/NativeMethods.c \ - $(LB_SRCDIR)/Table.c \ - $(LB_SRCDIR)/LitebaseGlobals.c \ - $(LB_SRCDIR)/Key.c \ - $(LB_SRCDIR)/Node.c \ - $(LB_SRCDIR)/Index.c \ - $(LB_SRCDIR)/SQLValue.c \ - $(LB_SRCDIR)/MarkBits.c \ - $(LB_SRCDIR)/MemoryFile.c \ - $(LB_SRCDIR)/NormalFile.c \ - $(LB_SRCDIR)/PreparedStatement.c \ - $(LB_SRCDIR)/UtilsLB.c - -PARSER_FILES = \ - $(LB_SRCDIR)/parser/LitebaseLex.c \ - $(LB_SRCDIR)/parser/LitebaseMessage.c \ - $(LB_SRCDIR)/parser/LitebaseParser.c \ - $(LB_SRCDIR)/parser/SQLBooleanClause.c \ - $(LB_SRCDIR)/parser/SQLBooleanClauseTree.c \ - $(LB_SRCDIR)/parser/SQLColumnListClause.c \ - $(LB_SRCDIR)/parser/SQLDeleteStatement.c \ - $(LB_SRCDIR)/parser/SQLInsertStatement.c \ - $(LB_SRCDIR)/parser/SQLSelectStatement.c \ - $(LB_SRCDIR)/parser/SQLUpdateStatement.c - -_TEST_SUITE ?= DISABLE - -ifeq ($(_TEST_SUITE),ENABLE) -TEST_SUITE_FILES = $(TC_SRCDIR)/tests/tc_testsuite.c -endif - -SOURCE_FILES = \ - $(LITEBASE_FILES) \ - $(PARSER_FILES) \ - $(TEST_SUITE_FILES) - -LOCAL_ARM_MODE := arm -LOCAL_MODULE := litebase -LOCAL_SRC_FILES := $(SOURCE_FILES) -LOCAL_C_INCLUDES := $(TC_INCLUDEDIR)/tcvm $(TC_INCLUDEDIR)/util $(TC_INCLUDEDIR)/nm/io $(TC_INCLUDEDIR)/nm/lang $(LB_INCLUDEDIR)/parser $(LB_INCLUDEDIR) -LOCAL_LDLIBS := -llog -ldl -LOCAL_CFLAGS := -DTOTALCROSS -DLB_EXPORTS -DFORCE_LIBC_ALLOC $(EXTRA_DEFINES) -LOCAL_LDFLAGS := -Wl,-Map,$(NDK_APP_DST_DIR)/$(LOCAL_MODULE).map -NDK_APP_DST_DIR := $(NDK_OUT)/libs/$(TARGET_ARCH_ABI) - -include $(BUILD_SHARED_LIBRARY) diff --git a/LitebaseSDK/src/native/jni/Application.mk b/LitebaseSDK/src/native/jni/Application.mk deleted file mode 100644 index f5a318f6ae..0000000000 --- a/LitebaseSDK/src/native/jni/Application.mk +++ /dev/null @@ -1,6 +0,0 @@ -APP_PROJECT_PATH := $(call my-dir) -APP_ABI := armeabi -APP_MODULES := litebase -APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk -APP_OPTIM := release -APP_CFLAGS += -O3 diff --git a/LitebaseSDK/src/native/jni/default.properties b/LitebaseSDK/src/native/jni/default.properties deleted file mode 100644 index 51e933a98f..0000000000 --- a/LitebaseSDK/src/native/jni/default.properties +++ /dev/null @@ -1,13 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "build.properties", and override values to adapt the script to your -# project structure. - -# Indicates whether an apk should be generated for each density. -split.density=false -# Project target. -target=android-8 diff --git a/LitebaseSDK/src/native/jni/libs/armeabi-v7a/.gitignore b/LitebaseSDK/src/native/jni/libs/armeabi-v7a/.gitignore deleted file mode 100644 index abdcae0a7d..0000000000 --- a/LitebaseSDK/src/native/jni/libs/armeabi-v7a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.so -/*.map diff --git a/LitebaseSDK/src/native/jni/libs/armeabi/.gitignore b/LitebaseSDK/src/native/jni/libs/armeabi/.gitignore deleted file mode 100644 index abdcae0a7d..0000000000 --- a/LitebaseSDK/src/native/jni/libs/armeabi/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/*.so -/*.map diff --git a/LitebaseSDK/src/native/jni/options_demo.mk b/LitebaseSDK/src/native/jni/options_demo.mk deleted file mode 100644 index de91bcb6b1..0000000000 --- a/LitebaseSDK/src/native/jni/options_demo.mk +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DEFINES = \ - -DENABLE_DEMO diff --git a/LitebaseSDK/src/native/jni/options_nodemo.mk b/LitebaseSDK/src/native/jni/options_nodemo.mk deleted file mode 100644 index 7679c83cd4..0000000000 --- a/LitebaseSDK/src/native/jni/options_nodemo.mk +++ /dev/null @@ -1 +0,0 @@ -EXTRA_DEFINES = diff --git a/LitebaseSDK/src/native/jni/options_noras.mk b/LitebaseSDK/src/native/jni/options_noras.mk deleted file mode 100644 index 473fcea1ed..0000000000 --- a/LitebaseSDK/src/native/jni/options_noras.mk +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DEFINES = \ - -DDISABLE_RAS diff --git a/LitebaseSDK/src/native/lbFile.c b/LitebaseSDK/src/native/lbFile.c deleted file mode 100644 index d6b7e8382a..0000000000 --- a/LitebaseSDK/src/native/lbFile.c +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This module defines useful functions for other Litebase modules. - */ - -#include "lbFile.h" - -#if defined WIN32 - #include "win/File_c.h" -#else - #include "posix/File_c.h" -#endif - -Err lbfileCreate(NATIVE_FILE* fref, TCHARP path, int32 mode) -{ - return fileCreate(fref, path, mode, null); -} -Err lbfileClose(NATIVE_FILE* fref) -{ - return fileClose(fref); -} -Err lbfileCreateDir(TCHARP path) -{ - return fileCreateDir(path, -1); -} -Err lbfileDelete(NATIVE_FILE* fref, TCHARP path, bool isOpen) -{ - return fileDelete(fref, path, -1, isOpen); -} -bool lbfileExists(TCHARP path) -{ - return fileExists(path, -1); -} -Err lbfileGetSize(NATIVE_FILE fref, TCHARP szPath, int32* size) -{ - return fileGetSize(fref, szPath, size); -} -Err lbfileReadBytes(NATIVE_FILE fref, CharP bytes, int32 offset, int32 length, int32* bytesRead) -{ - return fileReadBytes(fref, bytes, offset, length, bytesRead); -} -Err lbfileRename(NATIVE_FILE fref, TCHARP currPath, TCHARP newPath, bool isOpen) -{ - return fileRename(fref, -1, currPath, newPath, isOpen); -} -Err lbfileSetPos(NATIVE_FILE fref, int32 position) -{ - return fileSetPos(fref, position); -} -Err lbfileWriteBytes(NATIVE_FILE fref, CharP bytes, int32 offset, int32 length, int32* bytesWritten) -{ - return fileWriteBytes(fref, bytes, offset, length, bytesWritten); -} -Err lbfileSetSize(NATIVE_FILE* fref, int32 newSize) -{ - return fileSetSize(fref, newSize); -} -Err lbfileFlush(NATIVE_FILE fref) -{ - return fileFlush(fref); -} diff --git a/LitebaseSDK/src/native/lbFile.h b/LitebaseSDK/src/native/lbFile.h deleted file mode 100644 index c3c08738fe..0000000000 --- a/LitebaseSDK/src/native/lbFile.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * This module declares functions that handles files. - */ - -#ifndef LITEBASE_LBFILE_H -#define LITEBASE_LBFILE_H - -#include "Litebase.h" - -extern Err lbfileCreate(NATIVE_FILE* fref, TCHARP path, int32 mode); -extern Err lbfileClose(NATIVE_FILE* fref); -extern Err lbfileCreateDir(TCHARP path); -extern Err lbfileDelete(NATIVE_FILE* fref, TCHARP path, bool isOpen); -extern bool lbfileExists(TCHARP path); -extern Err lbfileGetSize(NATIVE_FILE fref, TCHARP szPath, int32* size); -extern Err lbfileReadBytes(NATIVE_FILE fref, CharP bytes, int32 offset, int32 length, int32* bytesRead); -extern Err lbfileRename(NATIVE_FILE fref, TCHARP currPath, TCHARP newPath, bool isOpen); -extern Err lbfileSetPos(NATIVE_FILE fref, int32 position); -extern Err lbfileWriteBytes(NATIVE_FILE fref, CharP bytes, int32 offset, int32 length, int32* bytesWritten); -extern Err lbfileSetSize(NATIVE_FILE* fref, int32 newSize); -extern Err lbfileFlush(NATIVE_FILE fref); - -#endif diff --git a/LitebaseSDK/src/native/nativeProcAddressesLB.c b/LitebaseSDK/src/native/nativeProcAddressesLB.c deleted file mode 100644 index 63e4119ae9..0000000000 --- a/LitebaseSDK/src/native/nativeProcAddressesLB.c +++ /dev/null @@ -1,123 +0,0 @@ -#include "tcvm.h" -#include "NativeMethods.h" -#include "utils.h" - -void fillNativeProcAddressesLB() -{ - htPutPtr(&htNativeProcAddresses, hashCode("lRI_next"), &lRI_next); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_nextNotSynced"), &lRI_nextNotSynced); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_setSynced"), &lRI_setSynced); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_setNotSynced"), &lRI_setNotSynced); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_close"), &lRI_close); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_reset"), &lRI_reset); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getShort_i"), &lRI_getShort_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getInt_i"), &lRI_getInt_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getLong_i"), &lRI_getLong_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getFloat_i"), &lRI_getFloat_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getDouble_i"), &lRI_getDouble_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getString_i"), &lRI_getString_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getBlob_i"), &lRI_getBlob_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getDate_i"), &lRI_getDate_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_getDateTime_i"), &lRI_getDateTime_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRI_isNull_i"), &lRI_isNull_i); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateGetInstance"), &lLC_privateGetInstance); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateGetInstance_s"), &lLC_privateGetInstance_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateGetInstance_ss"), &lLC_privateGetInstance_ss); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_getSourcePath"), &lLC_getSourcePath); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_execute_s"), &lLC_execute_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_executeUpdate_s"), &lLC_executeUpdate_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_executeQuery_s"), &lLC_executeQuery_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_prepareStatement_s"), &lLC_prepareStatement_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_getCurrentRowId_s"), &lLC_getCurrentRowId_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_getRowCount_s"), &lLC_getRowCount_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_setRowInc_si"), &lLC_setRowInc_si); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_exists_s"), &lLC_exists_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_closeAll"), &lLC_closeAll); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_purge_s"), &lLC_purge_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_getRowCountDeleted_s"), &lLC_getRowCountDeleted_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_getRowIterator_s"), &lLC_getRowIterator_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateGetLogger"), &lLC_privateGetLogger); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateSetLogger_l"), &lLC_privateSetLogger_l); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateGetDefaultLogger"), &lLC_privateGetDefaultLogger); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateDeleteLogFiles"), &lLC_privateDeleteLogFiles); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_privateProcessLogs_Ssb"), &lLC_privateProcessLogs_Ssb); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_recoverTable_s"), &lLC_recoverTable_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_convert_s"), &lLC_convert_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_getSlot"), &lLC_getSlot); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_isOpen_s"), &lLC_isOpen_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_dropDatabase_ssi"), &lLC_dropDatabase_ssi); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_isTableProperlyClosed_s"), &lLC_isTableProperlyClosed_s); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_listAllTables"), &lLC_listAllTables); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_encryptTables_ssi"), &lLC_encryptTables_ssi); - htPutPtr(&htNativeProcAddresses, hashCode("lLC_decryptTables_ssi"), &lLC_decryptTables_ssi); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getResultSetMetaData"), &lRS_getResultSetMetaData); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_close"), &lRS_close); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_beforeFirst"), &lRS_beforeFirst); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_afterLast"), &lRS_afterLast); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_first"), &lRS_first); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_last"), &lRS_last); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_next"), &lRS_next); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_prev"), &lRS_prev); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getShort_i"), &lRS_getShort_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getShort_s"), &lRS_getShort_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getInt_i"), &lRS_getInt_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getInt_s"), &lRS_getInt_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getLong_i"), &lRS_getLong_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getLong_s"), &lRS_getLong_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getFloat_i"), &lRS_getFloat_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getFloat_s"), &lRS_getFloat_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getDouble_i"), &lRS_getDouble_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getDouble_s"), &lRS_getDouble_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getChars_i"), &lRS_getChars_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getChars_s"), &lRS_getChars_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getString_i"), &lRS_getString_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getString_s"), &lRS_getString_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getBlob_i"), &lRS_getBlob_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getBlob_s"), &lRS_getBlob_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getStrings_i"), &lRS_getStrings_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getStrings"), &lRS_getStrings); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getDate_i"), &lRS_getDate_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getDate_s"), &lRS_getDate_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getDateTime_i"), &lRS_getDateTime_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getDateTime_s"), &lRS_getDateTime_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_absolute_i"), &lRS_absolute_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_relative_i"), &lRS_relative_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getRow"), &lRS_getRow); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_setDecimalPlaces_ii"), &lRS_setDecimalPlaces_ii); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_getRowCount"), &lRS_getRowCount); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_isNull_i"), &lRS_isNull_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_isNull_s"), &lRS_isNull_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRS_rowToString"), &lRS_rowToString); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnCount"), &lRSMD_getColumnCount); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnDisplaySize_i"), &lRSMD_getColumnDisplaySize_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnLabel_i"), &lRSMD_getColumnLabel_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnType_i"), &lRSMD_getColumnType_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnTypeName_i"), &lRSMD_getColumnTypeName_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnTableName_i"), &lRSMD_getColumnTableName_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getColumnTableName_s"), &lRSMD_getColumnTableName_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_hasDefaultValue_i"), &lRSMD_hasDefaultValue_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_hasDefaultValue_s"), &lRSMD_hasDefaultValue_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_isNotNull_i"), &lRSMD_isNotNull_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_isNotNull_s"), &lRSMD_isNotNull_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getPKColumnIndices_s"), &lRSMD_getPKColumnIndices_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getPKColumnNames_s"), &lRSMD_getPKColumnNames_s); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getDefaultValue_i"), &lRSMD_getDefaultValue_i); - htPutPtr(&htNativeProcAddresses, hashCode("lRSMD_getDefaultValue_s"), &lRSMD_getDefaultValue_s); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_executeQuery"), &lPS_executeQuery); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_executeUpdate"), &lPS_executeUpdate); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setShort_is"), &lPS_setShort_is); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setInt_ii"), &lPS_setInt_ii); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setLong_il"), &lPS_setLong_il); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setFloat_id"), &lPS_setFloat_id); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setDouble_id"), &lPS_setDouble_id); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setString_is"), &lPS_setString_is); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setBlob_iB"), &lPS_setBlob_iB); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setDate_id"), &lPS_setDate_id); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setDateTime_id"), &lPS_setDateTime_id); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setDateTime_it"), &lPS_setDateTime_it); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_setNull_i"), &lPS_setNull_i); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_clearParameters"), &lPS_clearParameters); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_toString"), &lPS_toString); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_close"), &lPS_close); - htPutPtr(&htNativeProcAddresses, hashCode("lPS_isValid"), &lPS_isValid); -} diff --git a/LitebaseSDK/src/native/nativeProcAddressesLB.h b/LitebaseSDK/src/native/nativeProcAddressesLB.h deleted file mode 100644 index 8ee971f5ca..0000000000 --- a/LitebaseSDK/src/native/nativeProcAddressesLB.h +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -#if defined (darwin) || defined (ANDROID) -void fillNativeProcAddressesLB(); -#else -#define fillNativeProcAddressesLB() -#endif diff --git a/LitebaseSDK/src/native/parser/LitebaseLex.c b/LitebaseSDK/src/native/parser/LitebaseLex.c deleted file mode 100644 index 38c6f75cd4..0000000000 --- a/LitebaseSDK/src/native/parser/LitebaseLex.c +++ /dev/null @@ -1,457 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions used by the lexical analizer. - */ - -#include "LitebaseLex.h" - -// juliana@253_9: improved Litebase parser. -/** - * The function which does the lexical analisys. - * - * @param parser The parser structure, which will hold the tree resulting from the parsing process. - * @return The token code. - */ -int32 yylex(LitebaseParser* parser) -{ - TRACE("yyLex") - int32 initialPos, - yybefore, - hash = parser->select.sqlHashCode; - JCharP zzReaderChars = parser->zzReaderChars; - - while (true) - { - while (parser->yycurrent <= ' ') // Skips blanks. - { - CALCULATE_HASH(parser->yycurrent); - GET_YYCURRENT(parser->yycurrent); - } - - // juliana@221_4: unicode strings were not dealt properly on Windows 32, Windows CE, Palm, iPhone, and Android. - // Ends the lexycal analysis if the end of the file is found - if (parser->yycurrent == 65535) - { - parser->select.sqlHashCode = hash; - return PARSER_EOF; - } - - if (is[parser->yycurrent] & IS_ALPHA)// Finds identifiers or reserved words. The first character must be a letter. - { - initialPos = parser->yyposition - 1; - while (parser->yycurrent <= 255 && is[parser->yycurrent] & IS_ALPHA_DIGIT) // The other characters must be a letter, digit or '_'. - { - CALCULATE_HASH(parser->yycurrent); - INSERT_CHAR(parser->yycurrent); - } - parser->select.sqlHashCode = hash; - return findReserved(parser, initialPos); // Sees if the identifier is a reserved word or just an identifier. - } - - if (is[parser->yycurrent] & IS_START_DIGIT) // Finds digits. The start of a digit can be a digit or a sign. - { - if (parser->yycurrent == '+') // juliana@226a_20: now passing +number (ex. +10) will work on sql clauses. - GET_YYCURRENT(parser->yycurrent); - - initialPos = parser->yyposition - 1; - CALCULATE_HASH('?'); - GET_YYCURRENT(parser->yycurrent); - while (parser->yycurrent <= 255 && is[parser->yycurrent] & IS_DIGIT) // The second part of the nunber must be digits. - INSERT_CHAR(parser->yycurrent); - - if (parser->yycurrent == '.') // Test if the number is not an integer. - { - INSERT_CHAR(parser->yycurrent); - while (parser->yycurrent <= 255 && is[parser->yycurrent] & IS_DIGIT) // Gets the rest of the digits of the non-integer number. - INSERT_CHAR(parser->yycurrent); - } - - if (parser->yycurrent <= 255 && is[parser->yycurrent] & IS_END_NUM) // The number may finish with a letter indicating its type. - INSERT_CHAR(parser->yycurrent); - - // Copies the number to yacc. - parser->yylval = TC_heapAlloc(parser->heap, (yybefore = (parser->yyposition - initialPos)) << 1); - xmemmove(parser->yylval, &zzReaderChars[initialPos], yybefore > 2? (yybefore - 1) << 1 : 2); - parser->select.sqlHashCode = hash; - return TK_NUMBER; - } - - if (is[parser->yycurrent] & IS_RELATIONAL) // Finds tokens with two characters, or '>' or '<'. - { - yybefore = parser->yycurrent; - CALCULATE_HASH(parser->yycurrent); - GET_YYCURRENT(parser->yycurrent); - - if ((yybefore == '!' && parser->yycurrent == '=') || (yybefore == '<' && parser->yycurrent == '>')) // Sees if it is the different operator. - { - CALCULATE_HASH(parser->yycurrent); - GET_YYCURRENT(parser->yycurrent); - parser->select.sqlHashCode = hash; - return TK_DIFF; - } - if (yybefore == '>' && parser->yycurrent == '=') // Sees if it is the greater or equal operator. - { - CALCULATE_HASH(parser->yycurrent); - GET_YYCURRENT(parser->yycurrent); - parser->select.sqlHashCode = hash; - return TK_GREATER_EQUAL; - } - if (yybefore == '<' && parser->yycurrent == '=') // Sees if it is the less operator. - { - CALCULATE_HASH(parser->yycurrent); - GET_YYCURRENT(parser->yycurrent); - parser->select.sqlHashCode = hash; - return TK_LESS_EQUAL; - } - if (yybefore == '>' || yybefore == '<') // > or <. - { - parser->select.sqlHashCode = hash; - return yybefore; - } - - // Invalid operator. - lbError(ERR_SYNTAX_ERROR, parser); - return PARSER_ERROR; - } - - // Finds tokens with one character, punctuators or '='. - // Sees if the tokens are arithmetic operators, '(', or ')'. In this case, returns the name of the token. - if (is[parser->yycurrent] & (IS_PUNCT | IS_OPERATOR)) - { - yybefore = parser->yycurrent; - CALCULATE_HASH(parser->yycurrent); - GET_YYCURRENT(parser->yycurrent); - parser->select.sqlHashCode = hash; - return yybefore; - } - - // Sees if the token is an escape. - // Sees if the token is a string of the type 'xxx'. - if (parser->yycurrent == '\'') - { - int32 counter = 0, - finalPos; - JCharP str16; - - initialPos = parser->yyposition; - CALCULATE_HASH('?'); - GET_YYCURRENT(parser->yycurrent); - - // juliana@225_6: a quote was not being correctly inserted in a string when not using prepared statements. - while (parser->yycurrent != '\'') - { - if (parser->yycurrent == '\\') // Sees if there is an escape in the string. - { - GET_YYCURRENT(parser->yycurrent); - INSERT_CHAR(parser->yycurrent); - counter++; - } - - // juliana@221_4: unicode strings were not dealt properly on Windows 32, Windows CE, Palm, iPhone, and Android. - else if (parser->yycurrent == 65535) // The string must be closed before the end of the file. - { - GET_YYCURRENT(parser->yycurrent); - parser->select.sqlHashCode = hash; - lbError(ERR_SYNTAX_ERROR, parser); - return PARSER_ERROR; - } - - else - INSERT_CHAR(parser->yycurrent); // Anything else can be inside the string. - } - INSERT_CHAR(parser->yycurrent); - str16 = parser->yylval = TC_heapAlloc(parser->heap, (parser->yyposition - initialPos - 1 - counter) << 1); - counter = 0; - finalPos = parser->yyposition - 2; - while (initialPos < finalPos) - { - if (zzReaderChars[initialPos] == '\\') - initialPos++; - str16[counter++] =zzReaderChars[initialPos++]; - } - parser->select.sqlHashCode = hash; - return TK_STR; - } - - // Error. - GET_YYCURRENT(parser->yycurrent); - parser->select.sqlHashCode = hash; - lbError(ERR_SYNTAX_ERROR, parser); - return PARSER_ERROR; - } -} - -/** - * The initializer of the lexical analyser. It initializes the reserved words hash table and the kinds of token table based on ascii code. - * - * @return false if the reserved words hash table allocation fails; true, otherwise. - */ -bool initLex() -{ - TRACE("initLex") - int32 length = 'z' - 'a' + 1; - - // Initiate the array to select the kind of token. - xmemzero(is, 255); - - // Give the values for the letters. - xmemset(&is['a'], IS_ALPHA, length); - xmemset(&is['A'], IS_ALPHA, length); - - // Letters denoting types of numbers can also be the end of the number. - is['d'] |= IS_END_NUM; - is['D'] |= IS_END_NUM; - is['f'] |= IS_END_NUM; - is['F'] |= IS_END_NUM; - is['l'] |= IS_END_NUM; - is['L'] |= IS_END_NUM; - - xmemset(&is['0'], IS_DIGIT, 10); // Gives the values for the digits. - is['_'] = IS_ALPHA_DIGIT; // '_' can be part of an identifier. - - // + and - can be operators or sign of numbers. - is['+'] = IS_SIGN; - is['-'] = IS_SIGN; - - // The other operators and brackets. - is['*'] = IS_OPERATOR; - is['('] = IS_OPERATOR; - is[')'] = IS_OPERATOR; - - // The symbols that can represent double tokens. - is['<'] = IS_RELATIONAL; - is['>'] = IS_RELATIONAL; - is['!'] = IS_RELATIONAL; - - // The symbols that are treated as punctuators and =. - is['.'] = IS_PUNCT; - is[','] = IS_PUNCT; - is['?'] = IS_PUNCT; - is['='] = IS_PUNCT; - - // Populates the table with the reserved words. - if ((reserved = TC_htNew(NUM_RESERVED + 1, null)).items) - { - TC_htPut32(&reserved, HT_ABS, TK_ABS); - TC_htPut32(&reserved, HT_ADD, TK_ADD); - TC_htPut32(&reserved, HT_ALTER, TK_ALTER); - TC_htPut32(&reserved, HT_AND, TK_AND); - TC_htPut32(&reserved, HT_AS, TK_AS); - TC_htPut32(&reserved, HT_ASC, TK_ASC); - TC_htPut32(&reserved, HT_AVG, TK_AVG); - TC_htPut32(&reserved, HT_BLOB, TK_BLOB); - TC_htPut32(&reserved, HT_BY, TK_BY); - TC_htPut32(&reserved, HT_CHAR, TK_CHAR); - TC_htPut32(&reserved, HT_COUNT, TK_COUNT); - TC_htPut32(&reserved, HT_CREATE, TK_CREATE); - TC_htPut32(&reserved, HT_DATE, TK_DATE); - TC_htPut32(&reserved, HT_DATETIME, TK_DATETIME); - TC_htPut32(&reserved, HT_DAY, TK_DAY); - TC_htPut32(&reserved, HT_DEFAULT, TK_DEFAULT); - TC_htPut32(&reserved, HT_DELETE, TK_DELETE); - TC_htPut32(&reserved, HT_DESC, TK_DESC); - TC_htPut32(&reserved, HT_DISTINCT, TK_DISTINCT); - TC_htPut32(&reserved, HT_DOUBLE, TK_DOUBLE); - TC_htPut32(&reserved, HT_DROP, TK_DROP); - TC_htPut32(&reserved, HT_FLOAT, TK_FLOAT); - TC_htPut32(&reserved, HT_FROM, TK_FROM); - TC_htPut32(&reserved, HT_GROUP, TK_GROUP); - TC_htPut32(&reserved, HT_HAVING, TK_HAVING); - TC_htPut32(&reserved, HT_HOUR, TK_HOUR); - TC_htPut32(&reserved, HT_INDEX, TK_INDEX); - TC_htPut32(&reserved, HT_INSERT, TK_INSERT); - TC_htPut32(&reserved, HT_INT, TK_INT); - TC_htPut32(&reserved, HT_INTO, TK_INTO); - TC_htPut32(&reserved, HT_IS, TK_IS); - TC_htPut32(&reserved, HT_KEY, TK_KEY); - TC_htPut32(&reserved, HT_LIKE, TK_LIKE); - TC_htPut32(&reserved, HT_LONG, TK_LONG); - TC_htPut32(&reserved, HT_LOWER, TK_LOWER); - TC_htPut32(&reserved, HT_MAX, TK_MAX); - TC_htPut32(&reserved, HT_MILLIS, TK_MILLIS); - TC_htPut32(&reserved, HT_MIN, TK_MIN); - TC_htPut32(&reserved, HT_MINUTE, TK_MINUTE); - TC_htPut32(&reserved, HT_MONTH, TK_MONTH); - TC_htPut32(&reserved, HT_NOCASE, TK_NOCASE); - TC_htPut32(&reserved, HT_NOT, TK_NOT); - TC_htPut32(&reserved, HT_NULL, TK_NULL) ; - TC_htPut32(&reserved, HT_ON, TK_ON); - TC_htPut32(&reserved, HT_OR, TK_OR); - TC_htPut32(&reserved, HT_ORDER, TK_ORDER); - TC_htPut32(&reserved, HT_PRIMARY, TK_PRIMARY); - TC_htPut32(&reserved, HT_RENAME, TK_RENAME); - TC_htPut32(&reserved, HT_SECOND, TK_SECOND); - TC_htPut32(&reserved, HT_SELECT, TK_SELECT); - TC_htPut32(&reserved, HT_SET, TK_SET); - TC_htPut32(&reserved, HT_SHORT, TK_SHORT); - TC_htPut32(&reserved, HT_SUM, TK_SUM); - TC_htPut32(&reserved, HT_TABLE, TK_TABLE); - TC_htPut32(&reserved, HT_TO, TK_TO); - TC_htPut32(&reserved, HT_UPDATE, TK_UPDATE); - TC_htPut32(&reserved, HT_UPPER, TK_UPPER); - TC_htPut32(&reserved, HT_VALUES, TK_VALUES); - TC_htPut32(&reserved, HT_VARCHAR, TK_VARCHAR); - TC_htPut32(&reserved, HT_WHERE, TK_WHERE); - TC_htPut32(&reserved, HT_YEAR, TK_YEAR); - return true; - } - return false; -} - -// juliana@253_9: improved Litebase parser. -/** - * Finds if the token is a reserved word or just an identifier. - * - * @param parser The parser structure, which will hold the tree resulting from the parsing process. - * @param initialPos The initial position of the current token in the SQL string. - * @return The code of a reserved word token or an identifirer token. - */ -int32 findReserved(LitebaseParser* parser, int32 initialPos) -{ - TRACE("findReserved") - int32 found = -1; - bool hasNumber = false; - char buf[MAX_RESERVED_SIZE]; - - TC_JCharP2CharPBuf(&parser->zzReaderChars[initialPos], (8 < (parser->yyposition - initialPos - 1))? 8 : parser->yyposition - initialPos - 1, buf); - TC_CharPToLower(buf); - - // juliana@213_7: using numbers in the identifiers may cause a colision with the reserved word hash table. - while (++found < 8 && buf[found]) - if (buf[found] >= '0' && buf[found] <= '9') - hasNumber = true; - - if (!hasNumber && (found = TC_htGet32Inv(&reserved, TC_hashCode(buf))) >= 0) - return found; - - parser->yylval = TC_heapAlloc(parser->heap, found = parser->yyposition - initialPos); - TC_JCharP2CharPBuf(&parser->zzReaderChars[initialPos], found - 1, parser->yylval); - TC_CharPToLower(parser->yylval); - return TK_IDENT; -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if the function initLex() initializes all the lex global variable properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(initLex) -{ - int32 i = '@'; - UNUSED(currentContext) - - while (++i < '[') // The values for the letters. - { - ASSERT2_EQUALS(I32, is[i] & IS_ALPHA, IS_ALPHA); - ASSERT2_EQUALS(I32, is[i + 32] & IS_ALPHA, IS_ALPHA); - } - - // Letters denoting types of numbers can also be the end of the number. - ASSERT2_EQUALS(I32, is['d'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['D'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['f'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['F'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['l'] & IS_END_NUM, IS_END_NUM); - ASSERT2_EQUALS(I32, is['L'] & IS_END_NUM, IS_END_NUM); - - // The values for the digits. - i = '/'; - while (++i < ':') - ASSERT2_EQUALS(U8, is[i], IS_DIGIT); - - ASSERT2_EQUALS(I32, is['_'] & (IS_ALPHA | IS_DIGIT), (IS_ALPHA | IS_DIGIT)); // '_' can be part of an identifier. - - // + and - can be operators or sign of numbers. - ASSERT2_EQUALS(U8, is['+'], IS_SIGN); - ASSERT2_EQUALS(U8, is['-'], IS_SIGN); - - // The other operators and brackets. - ASSERT2_EQUALS(U8, is['*'], IS_OPERATOR); - ASSERT2_EQUALS(U8, is['('], IS_OPERATOR); - ASSERT2_EQUALS(U8, is[')'], IS_OPERATOR); - - // The symbols that can represent double tokens. - ASSERT2_EQUALS(U8, is['<'], IS_RELATIONAL); - ASSERT2_EQUALS(U8, is['>'], IS_RELATIONAL); - ASSERT2_EQUALS(U8, is['!'], IS_RELATIONAL); - - // The symbols that are treated as punctuators and =. - ASSERT2_EQUALS(U8, is['.'], IS_PUNCT); - ASSERT2_EQUALS(U8, is[','], IS_PUNCT); - ASSERT2_EQUALS(U8, is['?'], IS_PUNCT); - ASSERT2_EQUALS(U8, is['='], IS_PUNCT); - - // Checks if the hash codes of the reserved words are in the hash table. - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("abs")), TK_ABS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("add")), TK_ADD); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("alter")), TK_ALTER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("and")), TK_AND); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("as")), TK_AS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("asc")), TK_ASC); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("avg")), TK_AVG); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("blob")), TK_BLOB); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("by")), TK_BY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("char")), TK_CHAR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("count")), TK_COUNT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("create")), TK_CREATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("date")), TK_DATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("datetime")), TK_DATETIME); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("day")), TK_DAY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("default")), TK_DEFAULT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("delete")), TK_DELETE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("desc")), TK_DESC); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("distinct")), TK_DISTINCT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("double")), TK_DOUBLE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("drop")), TK_DROP); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("float")), TK_FLOAT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("from")), TK_FROM); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("group")), TK_GROUP); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("having")), TK_HAVING); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("hour")), TK_HOUR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("index")), TK_INDEX); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("insert")), TK_INSERT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("int")), TK_INT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("into")), TK_INTO); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("is")), TK_IS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("key")), TK_KEY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("like")), TK_LIKE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("long")), TK_LONG); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("lower")), TK_LOWER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("max")), TK_MAX); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("millis")), TK_MILLIS); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("min")), TK_MIN); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("minute")), TK_MINUTE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("month")), TK_MONTH); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("nocase")), TK_NOCASE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("not")), TK_NOT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("null")), TK_NULL) ; - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("on")), TK_ON); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("or")), TK_OR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("order")), TK_ORDER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("primary")), TK_PRIMARY); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("rename")), TK_RENAME); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("second")), TK_SECOND); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("select")), TK_SELECT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("set")), TK_SET); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("short")), TK_SHORT); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("sum")), TK_SUM); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("table")), TK_TABLE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("to")), TK_TO); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("update")), TK_UPDATE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("upper")), TK_UPPER); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("values")), TK_VALUES); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("varchar")), TK_VARCHAR); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("where")), TK_WHERE); - ASSERT2_EQUALS(I32, TC_htGet32(&reserved, TC_hashCode("year")), TK_YEAR); - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/parser/LitebaseLex.h b/LitebaseSDK/src/native/parser/LitebaseLex.h deleted file mode 100644 index b3839017d2..0000000000 --- a/LitebaseSDK/src/native/parser/LitebaseLex.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions used by the lexical analizer. - */ - -#ifndef LITEBASE_LITEBASELEX_H -#define LITEBASE_LITEBASELEX_H - -#include "Litebase.h" - -/** - * The function which does the lexical analisys. - * - * @param parser The parser structure, which will hold the tree resulting from the parsing process. - * @return The token code. - */ -int32 yylex(LitebaseParser* parser); - -/** - * The initializer of the lexical analyser. It initializes the reserved words hash table and the kinds of token table based on ascii code. - * - * @return false if the reserved words hash table allocation fails; true, otherwise. - */ -bool initLex(); - -/** - * Finds if the token is a reserved word or just an identifier. - * - * @param parser The parser structure, which will hold the tree resulting from the parsing process. - * @param initialPos The initial position of the current token in the SQL string. - * @return The code of a reserved word token or an identifirer token. - */ -int32 findReserved(LitebaseParser* parser, int32 initialPos); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if the function initLex() initializes all the lex global variable properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_initLex(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/parser/LitebaseMessage.c b/LitebaseSDK/src/native/parser/LitebaseMessage.c deleted file mode 100644 index 0ac9d82aeb..0000000000 --- a/LitebaseSDK/src/native/parser/LitebaseMessage.c +++ /dev/null @@ -1,905 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions and globals used to display Litebase error messages. - */ - -#include "LitebaseMessage.h" - -/** - * Initializes the error message arrays. - */ -void initLitebaseMessage(void) -{ - TRACE("initLitebaseMessage") - - // Some errors have space at the end. This can't be changed. - - // English messages. - // General errors. - errorMsgs_en[ERR_MESSAGE_START] = "Error: "; - errorMsgs_en[ERR_MESSAGE_POSITION] = " Near position %d."; - errorMsgs_en[ERR_SYNTAX_ERROR] = "Syntax error."; - - // Limit errors. - errorMsgs_en[ERR_MAX_NUM_FIELDS_REACHED] = "Maximum number of different fields was reached."; - errorMsgs_en[ERR_MAX_NUM_PARAMS_REACHED] = "Maximum number of parameters in the 'WHERE/HAVING' clause was reached."; - errorMsgs_en[ERR_MAX_COMP_INDICES] = "Maximum number of composed indices 32 was reached."; - errorMsgs_en[ERR_MAX_TABLE_NAME_LENGTH] = "Table name too big: must be <= 23."; - errorMsgs_en[ERR_FIELDS_OVERFLOW] = "The maximum number of fields in a SELECT clause was exceeded."; - errorMsgs_en[ERR_FIELD_OVERFLOW_GROUPBY_ORDERBY] = "Maximum number of columns exceeded in the 'ORDER BY/GROUP BY' clause."; - - // Column errors. - errorMsgs_en[ERR_UNKNOWN_COLUMN] = "Unknown column %s."; - errorMsgs_en[ERR_INVALID_COLUMN_NAME] = "Invalid column name: %s."; - errorMsgs_en[ERR_INVALID_COLUMN_NUMBER] = "Invalid column number: %d."; - errorMsgs_en[ERR_COLUMN_DOESNOT_HAVE_AN_INDEX] = "The following column(s) does (do) not have an associated index %s."; - errorMsgs_en[ERR_AMBIGUOUS_COLUMN_NAME] = "Column name in field list is ambiguous: %s."; - errorMsgs_en[ERR_COLUMN_NOT_FOUND] = "Column not found %s."; - errorMsgs_en[ERR_DUPLICATED_COLUMN_NAME] = "Duplicated column name: %s."; - - // Primary key errors. - errorMsgs_en[ERR_PRIMARY_KEY_ALREADY_DEFINED] = "A primary key was already defined for this table."; - errorMsgs_en[ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY] = "Table does not have a primary key."; - errorMsgs_en[ERR_STATEMENT_CREATE_DUPLICATED_PK] = "Statement creates a duplicated primary key in %s."; - - // Type errors. - errorMsgs_en[ERR_INCOMPATIBLE_TYPES] = "Incompatible types."; - errorMsgs_en[ERR_FIELD_SIZE_IS_NOT_INT] = "Field size must be a positive interger value."; - errorMsgs_en[ERR_INVALID_NUMBER] = "Value %s is not a valid number for the desired type: %s."; - errorMsgs_en[ERR_DATA_TYPE_FUNCTION] = "Incompatible data type for the function call: %s"; - - // Number of fields errors. - errorMsgs_en[ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH] = "The number of fields does not match the number of values "; - errorMsgs_en[ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION] = "The given number of values does not match the table definition %d."; - - // Default value errors. - errorMsgs_en[ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER] = "Length of default value is bigger than column size."; - errorMsgs_en[ERR_NOT_NULL_DEFAULT] = "An added column declared as NOT NULL must have a not null default value."; - - // Driver errors. - errorMsgs_en[ERR_DRIVER_CLOSED] = "This driver instance was closed and cannot be used anymore. Please get a new instance of it."; - errorMsgs_en[ERR_RESULTSET_CLOSED] = "ResultSet already closed!"; - errorMsgs_en[ERR_RESULTSETMETADATA_CLOSED] = "ResultSetMetaData cannot be used after the ResultSet is closed."; - errorMsgs_en[ERR_INVALID_CRID] = "The application id must be four characters long."; - errorMsgs_en[ERR_INVALID_INC] = "The increment must be greater than 0 or -1."; - errorMsgs_en[ERR_ROWITERATOR_CLOSED] = "Iterator already closed."; - errorMsgs_en[ERR_PREPARED_STMT_CLOSED] = "Prepared statement closed. Please prepare it again."; - errorMsgs_en[ERR_INVALID_PARAMETER] = "Invalid connection parameter: %s."; - - // Table errors. - errorMsgs_en[ERR_TABLE_NAME_NOT_FOUND] = "Table name not found: %s."; - errorMsgs_en[ERR_TABLE_ALREADY_CREATED] = "Table already created: %s."; - errorMsgs_en[ERR_WRONG_STRING_FORMAT] = "It is not possible to open a table within a connection with a different string format."; - errorMsgs_en[ERR_WRONG_CRYPTO_FORMAT] = "It is not possible to open a table within a connection with a different cryptography format."; - - // ROWID errors. - errorMsgs_en[ERR_ROWID_CANNOT_BE_CHANGED] = "ROWID can't be changed by the user!"; - - // Prepared Statement errors. - errorMsgs_en[ERR_QUERY_DOESNOT_RETURN_RESULTSET] = "Query does not return result set."; - errorMsgs_en[ERR_QUERY_DOESNOT_PERFORM_UPDATE] = "Query does not perform updates in the database."; - errorMsgs_en[ERR_NOT_ALL_PARAMETERS_DEFINED] = "Not all parameters of the query had their values defined."; - errorMsgs_en[ERR_PARAMETER_NOT_DEFINED] = "A value was not defined for the parameter %d."; - errorMsgs_en[ERR_INVALID_PARAMETER_INDEX] = "Invalid parameter index."; - - // Rename errors. - errorMsgs_en[ERR_TABLE_ALREADY_EXIST] = "Can't rename table. This table already exists: %s."; - errorMsgs_en[ERR_COLUMN_ALREADY_EXIST] = "Column already exists: %s."; - - // Alias errors. - errorMsgs_en[ERR_NOT_UNIQUE_ALIAS_TABLE] = "Not unique table/alias: %s."; - errorMsgs_en[ERR_DUPLICATE_ALIAS] = "This alias is already being used in this expression: %s."; - errorMsgs_en[ERR_REQUIRED_ALIAS] = "An alias is required for the aggregate function column."; - - // Litebase.execute() error. - errorMsgs_en[ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED] = "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute()."; - - // Order by and group by errors. - errorMsgs_en[ERR_ORDER_GROUPBY_MUST_MATCH] = "ORDER BY and GROUP BY clauses must match."; - errorMsgs_en[ERR_VIRTUAL_COLUMN_ON_GROUPBY] = "No support for virtual columns in SQL queries with GROUP BY clause."; - - // Function errors. - errorMsgs_en[ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT] - = "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause."; - errorMsgs_en[ERR_IS_NOT_AGGREG_FUNCTION] - = "%s is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation functions."; - errorMsgs_en[ERR_CANNOT_MIX_AGGREG_FUNCTION] = "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause."; - errorMsgs_en[ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY] = "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause."; - errorMsgs_en[ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION] -= "%s was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation funtions."; - errorMsgs_en[ERR_SUM_AVG_WITH_DATE_DATETIME] = "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields."; - - // DATE and DATETIME errors. - errorMsgs_en[ERR_VALUE_ISNOT_DATE] = "Value is not a DATE: %s."; - errorMsgs_en[ERR_VALUE_ISNOT_DATETIME] = "Value is not a DATETIME: %s."; - - // Index error. - errorMsgs_en[ERR_INDEX_ALREADY_CREATED] = "Index already created for column %s."; - errorMsgs_en[ERR_DROP_PRIMARY_KEY] = "Can't drop a primary key index withdrop index."; - errorMsgs_en[ERR_INDEX_LARGE] = "Index too large. It can't have more than 65534 nodes."; - - // NOT NULL errors. - errorMsgs_en[ERR_PK_CANT_BE_NULL] = "Primary key can't have null."; - errorMsgs_en[ERR_FIELD_CANT_BE_NULL] = "Field can't be null: %s."; - errorMsgs_en[ERR_PARAM_NULL] = "A parameter in a where clause can't be null."; - - // Result set errors. - errorMsgs_en[ERR_RS_INV_POS] = "ResultSet in invalid record position: %d."; - errorMsgs_en[ERR_RS_DEC_PLACES_START] = "Invalid value for decimal places: %d. It must range from -1 to 40."; - - // File errors. - errorMsgs_en[ERR_CANT_READ] = "Can't read from table %s."; - errorMsgs_en[ERR_CANT_LOAD_NODE] = "Can't load leaf node!"; - errorMsgs_en[ERR_TABLE_CORRUPTED] = "Table is corrupted: %s."; - errorMsgs_en[ERR_TABLE_NOT_CLOSED] = "Table not closed properly: %s."; // juliana@220_2 - errorMsgs_en[ERR_TABLE_CLOSED] = "A properly closed table can't be used in recoverTable(): %s."; // juliana@222_2 - errorMsgs_en[ERR_IDX_RECORD_DEL] = "Can't find index record position on delete."; - errorMsgs_en[ERR_WRONG_VERSION] = "The table format (%d) is incompatible with Litebase version. Please update your tables."; - errorMsgs_en[ERR_WRONG_PREV_VERSION] = "The table format is not the previous one: %s."; // juliana@220_11 - errorMsgs_en[ERR_INVALID_PATH] = "Invalid path: %s."; // juliana@214_1 - errorMsgs_en[ERR_INVALID_POS] = "Invalid file position: %d."; - errorMsgs_en[ERR_DB_NOT_FOUND] = "Database not found."; // juliana@226_10 - errorMsgs_en[ERR_TABLE_OPENED] = "An opened table can't be recovered or converted: %s."; // juliana@230_12 - - // BLOB errors. - errorMsgs_en[ERR_BLOB_TOO_BIG] = "The total size of a blob can't be greater then 10 Mb."; - errorMsgs_en[ERR_INVALID_MULTIPLIER] = "This is not a valid size multiplier."; - errorMsgs_en[ERR_BLOB_PRIMARY_KEY] = "A blob type can't be part of a primary key."; - errorMsgs_en[ERR_BLOB_INDEX] = "A BLOB column can't be indexed."; - errorMsgs_en[ERR_BLOB_WHERE] = "A BLOB can't be in the where clause."; - errorMsgs_en[ERR_BLOB_STRING] = "A BLOB can't be converted to a string."; - errorMsgs_en[ERR_BLOB_ORDER_GROUP] = "Blobs types can't be in ORDER BY or GROUP BY clauses."; - errorMsgs_en[ERR_COMP_BLOBS] = "It is not possible to compare BLOBs."; - errorMsgs_en[ERR_BLOBS_PREPARED] = "It is only possible to insert or update a BLOB through prepared statements using setBlob()."; - - // Portuguese messages. - // General errors. - errorMsgs_pt[ERR_MESSAGE_START] = "Erro: "; - errorMsgs_pt[ERR_MESSAGE_POSITION] = " Pr�ximo � posi��o %s."; - errorMsgs_pt[ERR_SYNTAX_ERROR] = "Erro de sintaxe."; - - // Limit errors. - errorMsgs_pt[ERR_MAX_NUM_FIELDS_REACHED] = "N�mero m�ximo de campos diferentes foi alcan�ado."; - errorMsgs_pt[ERR_MAX_NUM_PARAMS_REACHED] = "N�mero m�ximo da lista de par�metros na cl�usula 'WHERE/HAVING' foi alcan�ado."; - errorMsgs_pt[ERR_MAX_COMP_INDICES] = "Numero m�ximo de �ndices compostos 32 foi alcan�ado."; - errorMsgs_pt[ERR_MAX_TABLE_NAME_LENGTH] = "Nome da tabela muito grande: deve ser <= 23"; - errorMsgs_pt[ERR_FIELDS_OVERFLOW] = "O n�mero m�ximo de campos na cl�usula SELECT foi excedido."; - errorMsgs_pt[ERR_FIELD_OVERFLOW_GROUPBY_ORDERBY] = "O n�mero m�ximo de campos na cl�usula 'ORDER BY/GROUP BY' foi excedido."; - - // Column errors. - errorMsgs_pt[ERR_UNKNOWN_COLUMN] = "Coluna desconhecida %s."; - errorMsgs_pt[ERR_INVALID_COLUMN_NAME] = "Nome de coluna inv�lido: %s."; - errorMsgs_pt[ERR_INVALID_COLUMN_NUMBER] = "N�mero de coluna inv�lido: %d."; - errorMsgs_pt[ERR_COLUMN_DOESNOT_HAVE_AN_INDEX] = "A(s) coluna(s) a seguir n�o tem (t�m) um ind�ce associado %s."; - errorMsgs_pt[ERR_AMBIGUOUS_COLUMN_NAME]= "Nome de coluna amb�guo: %s."; - errorMsgs_pt[ERR_COLUMN_NOT_FOUND] = "Coluna n�o encontrada: %s."; - errorMsgs_pt[ERR_DUPLICATED_COLUMN_NAME] = "Nome de coluna duplicado: %s."; - - // Primary key errors. - errorMsgs_pt[ERR_PRIMARY_KEY_ALREADY_DEFINED] = "Uma chave prim�ria j� foi definida para esta tabela."; - errorMsgs_pt[ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY] = "Tabela n�o tem chave prim�ria."; - errorMsgs_pt[ERR_STATEMENT_CREATE_DUPLICATED_PK] = "Comando cria uma chave prim�ria duplicada em %s."; - - // Type errors. - errorMsgs_pt[ERR_INCOMPATIBLE_TYPES] = "Tipos incompativeis."; - errorMsgs_pt[ERR_FIELD_SIZE_IS_NOT_INT] = "Tamanho do campo deve ser um valor inteiro positivo."; - errorMsgs_pt[ERR_INVALID_NUMBER] = "O valor %s n�o � um n�mero v�lido para o tipo desejado: %s."; - errorMsgs_pt[ERR_DATA_TYPE_FUNCTION] = "Tipo de dados incompat�vel para a chamada de fun��o: %s"; - - // Number of fields errors. - errorMsgs_pt[ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH] = "O n�mero de campos � diferente do n�mero de valores "; - errorMsgs_pt[ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION] = "O n�mero de valores dado n�o coincide com a defini��o da tabela %d."; - - // Default value errors. - errorMsgs_pt[ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER] = "Tamanho do valor padr�o � maior que o tamanho definido para a coluna."; - errorMsgs_pt[ERR_NOT_NULL_DEFAULT] = "Uma coluna adicionada declarada como NOT NULL deve ter um valor padr�o n�o nulo."; - - // Driver errors. - errorMsgs_pt[ERR_DRIVER_CLOSED] = "Esta inst�ncia do driver est� fechada e n�o pode ser mais utilizada. Por favor, obtenha uma nova inst�ncia."; - errorMsgs_pt[ERR_RESULTSET_CLOSED] = "ResultSet j� est� fechado!"; - errorMsgs_pt[ERR_RESULTSETMETADATA_CLOSED] = "ResultSetMetaData n�o pode ser usado depois que o ResultSet estiver fechado."; - errorMsgs_pt[ERR_INVALID_CRID] = "O id da aplica��o de ter 4 characteres."; - errorMsgs_pt[ERR_INVALID_INC] = "O incremento deve ser maior do que 0 ou -1."; - errorMsgs_pt[ERR_ROWITERATOR_CLOSED] = "Iterador j� foi fechado."; - errorMsgs_pt[ERR_PREPARED_STMT_CLOSED] = "Prepared statement fechado. Por favor, prepare-o novamente."; - errorMsgs_pt[ERR_INVALID_PARAMETER] = "Par�metro de conex�o inv�lido: %s."; - - // Table errors. - errorMsgs_pt[ERR_TABLE_NAME_NOT_FOUND] = "Nome da tabela n�o encontrado: %s."; - errorMsgs_pt[ERR_TABLE_ALREADY_CREATED] = "Tabela j� existe: %s."; - errorMsgs_pt[ERR_WRONG_STRING_FORMAT] = "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de strings diferente."; - errorMsgs_pt[ERR_WRONG_CRYPTO_FORMAT] = "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de criptografia diferente."; - - // ROWID errors. - errorMsgs_pt[ERR_ROWID_CANNOT_BE_CHANGED] = "ROWID n�o pode ser mudado pelo usu�rio!"; - - // Prepared Statement errors. - errorMsgs_pt[ERR_QUERY_DOESNOT_RETURN_RESULTSET] = "Comando SQL n�o retorna um ResultSet."; - errorMsgs_pt[ERR_QUERY_DOESNOT_PERFORM_UPDATE] = "Comando SQL n�o executa uma atualiza��o no banco de dados."; - errorMsgs_pt[ERR_NOT_ALL_PARAMETERS_DEFINED] = "Nem todos os par�metros da consulta tiveram seus valores definidos."; - errorMsgs_pt[ERR_PARAMETER_NOT_DEFINED] = "N�o foi definido um valor para o par�metro %d."; - errorMsgs_pt[ERR_INVALID_PARAMETER_INDEX] = "Invalid parameter index."; - - // Rename errors. - errorMsgs_pt[ERR_TABLE_ALREADY_EXIST] = "N�o � poss�vel renomear a tabela. Esta tabela j� existe: %s."; - errorMsgs_pt[ERR_COLUMN_ALREADY_EXIST] = "Coluna j� existe: %s."; - - // Alias errors. - errorMsgs_pt[ERR_NOT_UNIQUE_ALIAS_TABLE] = "Nome de tabela/alias repetido: %s."; - errorMsgs_pt[ERR_DUPLICATE_ALIAS] = "Este alias j� est� sendo utilizado no sql: %s."; - errorMsgs_pt[ERR_REQUIRED_ALIAS] = "Um alias � necess�rio para colunas com fun��o de agrega��o."; - - // Litebase.execute() error. - errorMsgs_pt[ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED] = "Apenas CREATE TABLE e CREATE INDEX s�o permitidos no Litebase.execute()"; - - // Order by and group by errors. - errorMsgs_pt[ERR_ORDER_GROUPBY_MUST_MATCH] = "Cl�usulas ORDER BY e GROUP BY devem coincidir."; - errorMsgs_pt[ERR_VIRTUAL_COLUMN_ON_GROUPBY] = "SQL com cl�usula GROUP BY n�o tem suporte para colunas virtuais."; - - // Function errors. - errorMsgs_pt[ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT] - = "Todas colunas que n�os�o fun��es de agrega��o na cl�usula SELECT devem estar na cl�usula GROUP BY."; - errorMsgs_pt[ERR_IS_NOT_AGGREG_FUNCTION] - = "%s n�o � uma fun��o de agrega��o. Todos as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."; - errorMsgs_pt[ERR_CANNOT_MIX_AGGREG_FUNCTION] = "N�o � possivel misturar colunas reais e de agrega��o no SELECT sem cl�usula GROUP BY."; - errorMsgs_pt[ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY] = "N�o � poss�vel ter fun��es de agrega��o com cl�usula ORDER BY sem cl�usula GROUP BY."; - errorMsgs_pt[ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION] - = "%s n�o foi listado no SELECT. Todas as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."; - errorMsgs_pt[ERR_SUM_AVG_WITH_DATE_DATETIME] = "Fun��es de agrega��o SUM e AVG n�o s�o usadas com colunas do tipo DATE e DATETIME."; - - // DATE and DATETIME errors. - errorMsgs_pt[ERR_VALUE_ISNOT_DATE] = "Valor n�o � um tipo DATE v�lido: %s."; - errorMsgs_pt[ERR_VALUE_ISNOT_DATETIME] = "Valor n�o � um tipo DATETIME v�lido: %s."; - - // Index error. - errorMsgs_pt[ERR_INDEX_ALREADY_CREATED] = "�ndice j� criado para a coluna %s."; - errorMsgs_pt[ERR_DROP_PRIMARY_KEY] = "N�o � poss�vel remover uma chave prim�ria usando drop index."; - errorMsgs_pt[ERR_INDEX_LARGE] = "�ndice muito grande. Ele n�o pode ter mais do que 65534 n�s."; - - // NOT NULL errors. - errorMsgs_pt[ERR_PK_CANT_BE_NULL] = "Chave prim�ria n�o pode ter NULL."; - errorMsgs_pt[ERR_FIELD_CANT_BE_NULL] = "Coluna n�o pode ser NULL: %s."; - errorMsgs_pt[ERR_PARAM_NULL] = "Um par�metro em uma where clause n�o pode ser NULL."; - - // Result set errors. - errorMsgs_pt[ERR_RS_INV_POS] = "ResultSet em uma posi��o de registro inv�lida %d."; - errorMsgs_pt[ERR_RS_DEC_PLACES_START] = "Valor inv�lido para casas decimais: %d. Deve ficar entre - 1 e 40."; - - // File errors. - errorMsgs_pt[ERR_CANT_READ] = "N�o � poss�vel ler da tabela %s."; - errorMsgs_pt[ERR_CANT_LOAD_NODE] = "N�o � poss�vel carregar n� folha!"; - errorMsgs_pt[ERR_TABLE_CORRUPTED] = "Tabela est� corrompida: %s."; - errorMsgs_pt[ERR_TABLE_NOT_CLOSED] = "Tabela n�o foi fechada corretamente: %s."; // juliana@220_2 - errorMsgs_pt[ERR_TABLE_CLOSED] = "Uma tabela fechada corretamente n�o pode ser usada no recoverTable(): %s."; // juliana@222_2 - errorMsgs_pt[ERR_IDX_RECORD_DEL] = "N�o � poss�vel achar a posi��o de registro no �ndice na exclus�o."; - errorMsgs_pt[ERR_WRONG_VERSION] = "O formato de tabela (%d) n�o � compat�vel com a vers�o do Litebase. Por favor, atualize suas tabelas."; - errorMsgs_pt[ERR_WRONG_PREV_VERSION] = "O formato de tabela n�o � o anterior: %s."; // juliana@220_11 - errorMsgs_pt[ERR_INVALID_PATH] = "Caminho inv�lido: %s."; // juliana@214_1 - errorMsgs_pt[ERR_INVALID_POS] = "Posi��o inv�lida no arquivo: %d."; - errorMsgs_pt[ERR_DB_NOT_FOUND] = "Base de dados n�o encontrada."; // juliana@226_10 - errorMsgs_pt[ERR_TABLE_OPENED] = "Uma tabela aberta n�o pode ser recuperada ou convertida: %s."; // juliana@230_12 - - // BLOB errors. - errorMsgs_pt[ERR_BLOB_TOO_BIG] = "O tamanho total de um BLOB n�o pode ser maior do que 10 Mb."; - errorMsgs_pt[ERR_INVALID_MULTIPLIER] = "O multiplicador de tamanho n�o � v�lido."; - errorMsgs_pt[ERR_BLOB_PRIMARY_KEY] = "Um tipo BLOB n�o pode ser parte de uma chave prim�ria."; - errorMsgs_pt[ERR_BLOB_INDEX] = "Uma coluna do tipo BLOB n�o pode ser indexada."; - errorMsgs_pt[ERR_BLOB_WHERE] = "Um BLOB n�o pode estar na cl�usula WHERE."; - errorMsgs_pt[ERR_BLOB_STRING] = "Um BLOB n�o pode ser convertido em uma string."; - errorMsgs_pt[ERR_BLOB_ORDER_GROUP] = "Tipos BLOB n�o podem estar em cl�usulas ORDER BY ou GROUP BY."; - errorMsgs_pt[ERR_COMP_BLOBS] = "N�o � poss�vel comparar BLOBs."; - errorMsgs_pt[ERR_BLOBS_PREPARED] = "S� � poss�vel inserir ou atualizar um BLOB atrav�s prepared statements usando setBlob()."; -} - -/** - * Gets the correct error message. - * - * @param messageNumber The error message code. - * @return The string with the desired error message. - */ -CharP getMessage(int32 messageNumber) -{ - TRACE("getMessage") - if (litebaseConnectionClass->i32StaticValues[4] == LANGUAGE_PT) - return errorMsgs_pt[messageNumber]; - return errorMsgs_en[messageNumber]; -} - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests that getMessage() returns the correct error message. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(getMessage) -{ - UNUSED(currentContext) - - // English messages. - // General errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_MESSAGE_START), "Error: "); - ASSERT2_EQUALS(Sz, getMessage(ERR_MESSAGE_POSITION), " Near position %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_SYNTAX_ERROR), "Syntax error."); - - // Limit errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_NUM_FIELDS_REACHED), "Maximum number of different fields was reached."); - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_NUM_PARAMS_REACHED), "Maximum number of parameters in the 'WHERE/HAVING' clause was reached."); - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_COMP_INDICES), "Maximum number of composed indices 32 was reached."); - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_TABLE_NAME_LENGTH), "Table name too big: must be <= 23."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELDS_OVERFLOW), "The maximum number of fields in a SELECT clause was exceeded."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELD_OVERFLOW_GROUPBY_ORDERBY), "Maximum number of columns exceeded in the 'ORDER BY/GROUP BY' clause."); - - // Column errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_UNKNOWN_COLUMN), "Unknown column %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_COLUMN_NAME), "Invalid column name: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_COLUMN_NUMBER), "Invalid column number: %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COLUMN_DOESNOT_HAVE_AN_INDEX), "The following column(s) does (do) not have an associated index %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_AMBIGUOUS_COLUMN_NAME), "Column name in field list is ambiguous: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COLUMN_NOT_FOUND), "Column not found %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DUPLICATED_COLUMN_NAME), "Duplicated column name: %s."); - - // Primary key errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_PRIMARY_KEY_ALREADY_DEFINED), "A primary key was already defined for this table."); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY), "Table does not have a primary key."); - ASSERT2_EQUALS(Sz, getMessage(ERR_STATEMENT_CREATE_DUPLICATED_PK), "Statement creates a duplicated primary key in %s."); - - // Type errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_INCOMPATIBLE_TYPES), "Incompatible types."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELD_SIZE_IS_NOT_INT), "Field size must be a positive interger value."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_NUMBER), "Value %s is not a valid number for the desired type: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DATA_TYPE_FUNCTION), "Incompatible data type for the function call: %s"); - - // Number of fields errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH), "The number of fields does not match the number of values "); - ASSERT2_EQUALS(Sz, getMessage(ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION), "The given number of values does not match the table definition %d."); - - // Default value errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER), "Length of default value is bigger than column size."); - ASSERT2_EQUALS(Sz, getMessage(ERR_NOT_NULL_DEFAULT), "An added column declared as NOT NULL must have a not null default value."); - - // Driver errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_DRIVER_CLOSED), "This driver instance was closed and cannot be used anymore. Please get a new instance of it."); - ASSERT2_EQUALS(Sz, getMessage(ERR_RESULTSET_CLOSED), "ResultSet already closed!"); - ASSERT2_EQUALS(Sz, getMessage(ERR_RESULTSETMETADATA_CLOSED), "ResultSetMetaData cannot be used after the ResultSet is closed."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_CRID), "The application id must be four characters long."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_INC), "The increment must be greater than 0 or -1."); - ASSERT2_EQUALS(Sz, getMessage(ERR_ROWITERATOR_CLOSED), "Iterator already closed."); - ASSERT2_EQUALS(Sz, getMessage(ERR_PREPARED_STMT_CLOSED), "Prepared statement closed. Please prepare it again."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_PARAMETER), "Invalid connection parameter: %s."); - - // Table errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_NAME_NOT_FOUND), "Table name not found: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_ALREADY_CREATED), "Table already created: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_STRING_FORMAT), "It is not possible to open a table within a connection with a different string format."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_CRYPTO_FORMAT), "It is not possible to open a table within a connection with a different cryptography format."); - - // ROWID errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_ROWID_CANNOT_BE_CHANGED), "ROWID can't be changed by the user!"); - - // Prepared Statement errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_QUERY_DOESNOT_RETURN_RESULTSET), "Query does not return result set."); - ASSERT2_EQUALS(Sz, getMessage(ERR_QUERY_DOESNOT_PERFORM_UPDATE), "Query does not perform updates in the database."); - ASSERT2_EQUALS(Sz, getMessage(ERR_NOT_ALL_PARAMETERS_DEFINED), "Not all parameters of the query had their values defined."); - ASSERT2_EQUALS(Sz, getMessage(ERR_PARAMETER_NOT_DEFINED), "A value was not defined for the parameter %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_PARAMETER_INDEX), "Invalid parameter index."); - - // Rename errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_ALREADY_EXIST), "Can't rename table. This table already exists: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COLUMN_ALREADY_EXIST), "Column already exists: %s."); - - // Alias errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_NOT_UNIQUE_ALIAS_TABLE), "Not unique table/alias: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DUPLICATE_ALIAS), "This alias is already being used in this expression: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_REQUIRED_ALIAS), "An alias is required for the aggregate function column."); - - // Litebase.execute() error. - ASSERT2_EQUALS(Sz, getMessage(ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED), "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute()."); - - // Order by and group by errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_ORDER_GROUPBY_MUST_MATCH), "ORDER BY and GROUP BY clauses must match."); - ASSERT2_EQUALS(Sz, getMessage(ERR_VIRTUAL_COLUMN_ON_GROUPBY), "No support for virtual columns in SQL queries with GROUP BY clause."); - - // Function errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT), - "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause."); - ASSERT2_EQUALS(Sz, getMessage(ERR_IS_NOT_AGGREG_FUNCTION), - "%s is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation functions."); - ASSERT2_EQUALS(Sz, getMessage(ERR_CANNOT_MIX_AGGREG_FUNCTION), - "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause."); - ASSERT2_EQUALS(Sz, getMessage(ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY), - "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION), -"%s was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation funtions." -); - ASSERT2_EQUALS(Sz, getMessage(ERR_SUM_AVG_WITH_DATE_DATETIME), - "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields."); - - // DATE and DATETIME errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_VALUE_ISNOT_DATE), "Value is not a DATE: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_VALUE_ISNOT_DATETIME), "Value is not a DATETIME: %s."); - - // Index error. - ASSERT2_EQUALS(Sz, getMessage(ERR_INDEX_ALREADY_CREATED), "Index already created for column %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DROP_PRIMARY_KEY), "Can't drop a primary key index withdrop index."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INDEX_LARGE), "Index too large. It can't have more than 65534 nodes."); - - // NOT NULL errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_PK_CANT_BE_NULL), "Primary key can't have null."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELD_CANT_BE_NULL), "Field can't be null: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_PARAM_NULL), "A parameter in a where clause can't be null."); - - // Result set errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_RS_INV_POS), "ResultSet in invalid record position: %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_RS_DEC_PLACES_START), "Invalid value for decimal places: %d. It must range from -1 to 40."); - - // File errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_CANT_READ), "Can't read from table %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_CANT_LOAD_NODE), "Can't load leaf node!"); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_CORRUPTED), "Table is corrupted: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_NOT_CLOSED), "Table not closed properly: %s."); // juliana@220_2 - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_CLOSED), "A properly closed table can't be used in recoverTable(): %s."); // juliana@222_2 - ASSERT2_EQUALS(Sz, getMessage(ERR_IDX_RECORD_DEL), "Can't find index record position on delete."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_VERSION), "The table format (%d) is incompatible with Litebase version. Please update your tables."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_PREV_VERSION), "The table format is not the previous one: %s."); // juliana@220_11 - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_PATH), "Invalid path: %s."); // juliana@214_1 - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_POS), "Invalid file position: %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DB_NOT_FOUND), "Database not found."); // juliana@226_10 - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_OPENED), "An opened table can't be recovered or converted: %s."); // juliana@230_12 - - // BLOB errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_TOO_BIG), "The total size of a blob can't be greater then 10 Mb."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_MULTIPLIER), "This is not a valid size multiplier."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_PRIMARY_KEY), "A blob type can't be part of a primary key."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_INDEX), "A BLOB column can't be indexed."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_WHERE), "A BLOB can't be in the where clause."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_STRING), "A BLOB can't be converted to a string."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_ORDER_GROUP), "Blobs types can't be in ORDER BY or GROUP BY clauses."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COMP_BLOBS), "It is not possible to compare BLOBs."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOBS_PREPARED), "It is only possible to insert or update a BLOB through prepared statements using setBlob()."); - - // Portuguese messages. - litebaseConnectionClass->i32StaticValues[4] = LANGUAGE_PT; - - // General errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_MESSAGE_START), "Erro: "); - ASSERT2_EQUALS(Sz, getMessage(ERR_MESSAGE_POSITION), " Pr�ximo � posi��o %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_SYNTAX_ERROR), "Erro de sintaxe."); - - // Limit errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_NUM_FIELDS_REACHED), "N�mero m�ximo de campos diferentes foi alcan�ado."); - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_NUM_PARAMS_REACHED), "N�mero m�ximo da lista de par�metros na cl�usula 'WHERE/HAVING' foi alcan�ado."); - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_COMP_INDICES), "Numero m�ximo de �ndices compostos 32 foi alcan�ado."); - ASSERT2_EQUALS(Sz, getMessage(ERR_MAX_TABLE_NAME_LENGTH), "Nome da tabela muito grande: deve ser <= 23"); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELDS_OVERFLOW), "O n�mero m�ximo de campos na cl�usula SELECT foi excedido."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELD_OVERFLOW_GROUPBY_ORDERBY), "O n�mero m�ximo de campos na cl�usula 'ORDER BY/GROUP BY' foi excedido."); - - // Column errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_UNKNOWN_COLUMN), "Coluna desconhecida %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_COLUMN_NAME), "Nome de coluna inv�lido: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_COLUMN_NUMBER), "N�mero de coluna inv�lido: %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COLUMN_DOESNOT_HAVE_AN_INDEX), "A(s) coluna(s) a seguir n�o tem (t�m) um ind�ce associado %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_AMBIGUOUS_COLUMN_NAME), "Nome de coluna amb�guo: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COLUMN_NOT_FOUND), "Coluna n�o encontrada: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DUPLICATED_COLUMN_NAME), "Nome de coluna duplicado: %s."); - - // Primary key errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_PRIMARY_KEY_ALREADY_DEFINED), "Uma chave prim�ria j� foi definida para esta tabela."); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_DOESNOT_HAVE_PRIMARY_KEY), "Tabela n�o tem chave prim�ria."); - ASSERT2_EQUALS(Sz, getMessage(ERR_STATEMENT_CREATE_DUPLICATED_PK), "Comando cria uma chave prim�ria duplicada em %s."); - - // Type errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_INCOMPATIBLE_TYPES), "Tipos incompativeis."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELD_SIZE_IS_NOT_INT), "Tamanho do campo deve ser um valor inteiro positivo."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_NUMBER), "O valor %s n�o � um n�mero v�lido para o tipo desejado: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DATA_TYPE_FUNCTION), "Tipo de dados incompat�vel para a chamada de fun��o: %s"); - - // Number of fields errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH), "O n�mero de campos � diferente do n�mero de valores "); - ASSERT2_EQUALS(Sz, getMessage(ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION), "O n�mero de valores dado n�o coincide com a defini��o da tabela %d."); - - // Default value errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_LENGTH_DEFAULT_VALUE_IS_BIGGER), "Tamanho do valor padr�o � maior que o tamanho definido para a coluna."); - ASSERT2_EQUALS(Sz, getMessage(ERR_NOT_NULL_DEFAULT), "Uma coluna adicionada declarada como NOT NULL deve ter um valor padr�o n�o nulo."); - - // Driver errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_DRIVER_CLOSED), - "Esta inst�ncia do driver est� fechada e n�o pode ser mais utilizada. Por favor, obtenha uma nova inst�ncia."); - ASSERT2_EQUALS(Sz, getMessage(ERR_RESULTSET_CLOSED), "ResultSet j� est� fechado!"); - ASSERT2_EQUALS(Sz, getMessage(ERR_RESULTSETMETADATA_CLOSED), "ResultSetMetaData n�o pode ser usado depois que o ResultSet estiver fechado."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_CRID), "O id da aplica��o de ter 4 characteres."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_INC), "O incremento deve ser maior do que 0 ou -1."); - ASSERT2_EQUALS(Sz, getMessage(ERR_ROWITERATOR_CLOSED), "Iterador j� foi fechado."); - ASSERT2_EQUALS(Sz, getMessage(ERR_PREPARED_STMT_CLOSED), "Prepared statement fechado. Por favor, prepare-o novamente."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_PARAMETER), "Par�metro de conex�o inv�lido: %s."); - - // Table errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_NAME_NOT_FOUND), "Nome da tabela n�o encontrado: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_ALREADY_CREATED), "Tabela j� existe: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_STRING_FORMAT), "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de strings diferente."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_CRYPTO_FORMAT), "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de criptografia diferente."); - - // ROWID errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_ROWID_CANNOT_BE_CHANGED), "ROWID n�o pode ser mudado pelo usu�rio!"); - - // Prepared Statement errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_QUERY_DOESNOT_RETURN_RESULTSET), "Comando SQL n�o retorna um ResultSet."); - ASSERT2_EQUALS(Sz, getMessage(ERR_QUERY_DOESNOT_PERFORM_UPDATE), "Comando SQL n�o executa uma atualiza��o no banco de dados."); - ASSERT2_EQUALS(Sz, getMessage(ERR_NOT_ALL_PARAMETERS_DEFINED), "Nem todos os par�metros da consulta tiveram seus valores definidos."); - ASSERT2_EQUALS(Sz, getMessage(ERR_PARAMETER_NOT_DEFINED), "N�o foi definido um valor para o par�metro %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_PARAMETER_INDEX), "Invalid parameter index."); - - // Rename errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_ALREADY_EXIST), "N�o � poss�vel renomear a tabela. Esta tabela j� existe: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COLUMN_ALREADY_EXIST), "Coluna j� existe: %s."); - - // Alias errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_NOT_UNIQUE_ALIAS_TABLE), "Nome de tabela/alias repetido: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DUPLICATE_ALIAS), "Este alias j� est� sendo utilizado no sql: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_REQUIRED_ALIAS), "Um alias � necess�rio para colunas com fun��o de agrega��o."); - - // Litebase.execute() error. - ASSERT2_EQUALS(Sz, getMessage(ERR_ONLY_CREATE_TABLE_INDEX_IS_ALLOWED), "Apenas CREATE TABLE e CREATE INDEX s�o permitidos no Litebase.execute()"); - - // Order by and group by errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_ORDER_GROUPBY_MUST_MATCH), "Cl�usulas ORDER BY e GROUP BY devem coincidir."); - ASSERT2_EQUALS(Sz, getMessage(ERR_VIRTUAL_COLUMN_ON_GROUPBY), "SQL com cl�usula GROUP BY n�o tem suporte para colunas virtuais."); - - // Function errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT), - "Todas colunas que n�os�o fun��es de agrega��o na cl�usula SELECT devem estar na cl�usula GROUP BY."); - ASSERT2_EQUALS(Sz, getMessage(ERR_IS_NOT_AGGREG_FUNCTION), - "%s n�o � uma fun��o de agrega��o. Todos as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."); - ASSERT2_EQUALS(Sz, getMessage(ERR_CANNOT_MIX_AGGREG_FUNCTION), - "N�o � possivel misturar colunas reais e de agrega��o no SELECT sem cl�usula GROUP BY."); - ASSERT2_EQUALS(Sz, getMessage(ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY), - "N�o � poss�vel ter fun��es de agrega��o com cl�usula ORDER BY sem cl�usula GROUP BY."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION), - "%s n�o foi listado no SELECT. Todas as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."); - ASSERT2_EQUALS(Sz, getMessage(ERR_SUM_AVG_WITH_DATE_DATETIME), - "Fun��es de agrega��o SUM e AVG n�o s�o usadas com colunas do tipo DATE e DATETIME."); - - // DATE and DATETIME errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_VALUE_ISNOT_DATE), "Valor n�o � um tipo DATE v�lido: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_VALUE_ISNOT_DATETIME), "Valor n�o � um tipo DATETIME v�lido: %s."); - - // Index error. - ASSERT2_EQUALS(Sz, getMessage(ERR_INDEX_ALREADY_CREATED), "�ndice j� criado para a coluna %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DROP_PRIMARY_KEY), "N�o � poss�vel remover uma chave prim�ria usando drop index."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INDEX_LARGE), "�ndice muito grande. Ele n�o pode ter mais do que 65534 n�s."); - - // NOT NULL errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_PK_CANT_BE_NULL), "Chave prim�ria n�o pode ter NULL."); - ASSERT2_EQUALS(Sz, getMessage(ERR_FIELD_CANT_BE_NULL), "Coluna n�o pode ser NULL: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_PARAM_NULL), "Um par�metro em uma where clause n�o pode ser NULL."); - - // Result set errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_RS_INV_POS), "ResultSet em uma posi��o de registro inv�lida %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_RS_DEC_PLACES_START), "Valor inv�lido para casas decimais: %d. Deve ficar entre - 1 e 40."); - - // File errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_CANT_READ), "N�o � poss�vel ler da tabela %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_CANT_LOAD_NODE), "N�o � poss�vel carregar n� folha!"); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_CORRUPTED), "Tabela est� corrompida: %s."); - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_NOT_CLOSED), "Tabela n�o foi fechada corretamente: %s."); // juliana@220_2 - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_CLOSED), "Uma tabela fechada corretamente n�o pode ser usada no recoverTable(): %s."); // juliana@222_2 - ASSERT2_EQUALS(Sz, getMessage(ERR_IDX_RECORD_DEL), "N�o � poss�vel achar a posi��o de registro no �ndice na exclus�o."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_VERSION), - "O formato de tabela (%d) n�o � compat�vel com a vers�o do Litebase. Por favor, atualize suas tabelas."); - ASSERT2_EQUALS(Sz, getMessage(ERR_WRONG_PREV_VERSION), "O formato de tabela n�o � o anterior: %s."); // juliana@220_11 - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_PATH), "Caminho inv�lido: %s."); // juliana@214_1 - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_POS), "Posi��o inv�lida no arquivo: %d."); - ASSERT2_EQUALS(Sz, getMessage(ERR_DB_NOT_FOUND), "Base de dados n�o encontrada."); // juliana@226_10 - ASSERT2_EQUALS(Sz, getMessage(ERR_TABLE_OPENED), "Uma tabela aberta n�o pode ser recuperada ou convertida: %s."); // juliana@230_12 - - // BLOB errors. - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_TOO_BIG), "O tamanho total de um BLOB n�o pode ser maior do que 10 Mb."); - ASSERT2_EQUALS(Sz, getMessage(ERR_INVALID_MULTIPLIER), "O multiplicador de tamanho n�o � v�lido."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_PRIMARY_KEY), "Um tipo BLOB n�o pode ser parte de uma chave prim�ria."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_INDEX), "Uma coluna do tipo BLOB n�o pode ser indexada."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_WHERE), "Um BLOB n�o pode estar na cl�usula WHERE."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_STRING), "Um BLOB n�o pode ser convertido em uma string."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOB_ORDER_GROUP), "Tipos BLOB n�o podem estar em cl�usulas ORDER BY ou GROUP BY."); - ASSERT2_EQUALS(Sz, getMessage(ERR_COMP_BLOBS), "N�o � poss�vel comparar BLOBs."); - ASSERT2_EQUALS(Sz, getMessage(ERR_BLOBS_PREPARED), "S� � poss�vel inserir ou atualizar um BLOB atrav�s prepared statements usando setBlob()."); - - litebaseConnectionClass->i32StaticValues[4] = LANGUAGE_EN; - -finish: ; -} - -/** - * Tests that initLitebaseMessage() correctly initializes the error messages. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(initLitebaseMessage) -{ - UNUSED(currentContext) - - // English messages. - // General errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[0], "Error: "); - ASSERT2_EQUALS(Sz, errorMsgs_en[1], " Near position %d."); - ASSERT2_EQUALS(Sz, errorMsgs_en[2], "Syntax error."); - - // Limit errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[3], "Maximum number of different fields was reached."); - ASSERT2_EQUALS(Sz, errorMsgs_en[4], "Maximum number of parameters in the 'WHERE/HAVING' clause was reached."); - ASSERT2_EQUALS(Sz, errorMsgs_en[5], "Maximum number of composed indices 32 was reached."); - ASSERT2_EQUALS(Sz, errorMsgs_en[6], "Table name too big: must be <= 23."); - ASSERT2_EQUALS(Sz, errorMsgs_en[7], "The maximum number of fields in a SELECT clause was exceeded."); - ASSERT2_EQUALS(Sz, errorMsgs_en[8], "Maximum number of columns exceeded in the 'ORDER BY/GROUP BY' clause."); - - // Column errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[9], "Unknown column %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[10], "Invalid column name: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[11], "Invalid column number: %d."); - ASSERT2_EQUALS(Sz, errorMsgs_en[12], "The following column(s) does (do) not have an associated index %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[13], "Column name in field list is ambiguous: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[14], "Column not found %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[15], "Duplicated column name: %s."); - - // Primary key errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[16], "A primary key was already defined for this table."); - ASSERT2_EQUALS(Sz, errorMsgs_en[17], "Table does not have a primary key."); - ASSERT2_EQUALS(Sz, errorMsgs_en[18], "Statement creates a duplicated primary key in %s."); - - // Type errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[19], "Incompatible types."); - ASSERT2_EQUALS(Sz, errorMsgs_en[20], "Field size must be a positive interger value."); - ASSERT2_EQUALS(Sz, errorMsgs_en[21], "Value %s is not a valid number for the desired type: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[22], "Incompatible data type for the function call: %s"); - - // Number of fields errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[23], "The number of fields does not match the number of values "); - ASSERT2_EQUALS(Sz, errorMsgs_en[24], "The given number of values does not match the table definition %d."); - - // Default value errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[25], "Length of default value is bigger than column size."); - ASSERT2_EQUALS(Sz, errorMsgs_en[26], "An added column declared as NOT NULL must have a not null default value."); - - // Driver errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[27], "This driver instance was closed and cannot be used anymore. Please get a new instance of it."); - ASSERT2_EQUALS(Sz, errorMsgs_en[28], "ResultSet already closed!"); - ASSERT2_EQUALS(Sz, errorMsgs_en[29], "ResultSetMetaData cannot be used after the ResultSet is closed."); - ASSERT2_EQUALS(Sz, errorMsgs_en[30], "The application id must be four characters long."); - ASSERT2_EQUALS(Sz, errorMsgs_en[31], "The increment must be greater than 0 or -1."); - ASSERT2_EQUALS(Sz, errorMsgs_en[32], "Iterator already closed."); - ASSERT2_EQUALS(Sz, errorMsgs_en[33], "Prepared statement closed. Please prepare it again."); - ASSERT2_EQUALS(Sz, errorMsgs_en[34], "Invalid connection parameter: %s."); - - // Table errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[35], "Table name not found: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[36], "Table already created: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[37], "It is not possible to open a table within a connection with a different string format."); - ASSERT2_EQUALS(Sz, errorMsgs_en[38], "It is not possible to open a table within a connection with a different cryptography format."); - - // ROWID errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[39], "ROWID can't be changed by the user!"); - - // Prepared Statement errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[40], "Query does not return result set."); - ASSERT2_EQUALS(Sz, errorMsgs_en[41], "Query does not perform updates in the database."); - ASSERT2_EQUALS(Sz, errorMsgs_en[42], "Not all parameters of the query had their values defined."); - ASSERT2_EQUALS(Sz, errorMsgs_en[43], "A value was not defined for the parameter %d."); - ASSERT2_EQUALS(Sz, errorMsgs_en[44], "Invalid parameter index."); - - // Rename errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[45], "Can't rename table. This table already exists: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[46], "Column already exists: %s."); - - // Alias errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[47], "Not unique table/alias: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[48], "This alias is already being used in this expression: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[49], "An alias is required for the aggregate function column."); - - // Litebase.execute() error. - ASSERT2_EQUALS(Sz, errorMsgs_en[50], "Only CREATE TABLE and CREATE INDEX can be used in Litebase.execute()."); - - // Order by and group by errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[51], "ORDER BY and GROUP BY clauses must match."); - ASSERT2_EQUALS(Sz, errorMsgs_en[52], "No support for virtual columns in SQL queries with GROUP BY clause."); - - // Function errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[53], "All non-aggregation function columns in the SELECT clause must also be in the GROUP BY clause."); - ASSERT2_EQUALS(Sz, errorMsgs_en[54], - "%s is not an aggregation function. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation functions."); - ASSERT2_EQUALS(Sz, errorMsgs_en[55], "Can't mix aggregation functions with real columns in the SELECT clause without a GROUP BY clause."); - ASSERT2_EQUALS(Sz, errorMsgs_en[56], "Can't have aggregation functions with ORDER BY clause and no GROUP BY clause."); - ASSERT2_EQUALS(Sz, errorMsgs_en[57], - "%s was not listed in the SELECT clause. All fields present in a HAVING clause must be listed in the SELECT clause as aliased aggregation funtions." -); - ASSERT2_EQUALS(Sz, errorMsgs_en[58], "SUM and AVG aggregation functions are not used with DATE and DATETIME type fields."); - - // DATE and DATETIME errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[59], "Value is not a DATE: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[60], "Value is not a DATETIME: %s."); - - // Index error. - ASSERT2_EQUALS(Sz, errorMsgs_en[61], "Index already created for column %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[62], "Can't drop a primary key index withdrop index."); - ASSERT2_EQUALS(Sz, errorMsgs_en[63], "Index too large. It can't have more than 65534 nodes."); - - // NOT NULL errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[64], "Primary key can't have null."); - ASSERT2_EQUALS(Sz, errorMsgs_en[65], "Field can't be null: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[66], "A parameter in a where clause can't be null."); - - // Result set errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[67], "ResultSet in invalid record position: %d."); - ASSERT2_EQUALS(Sz, errorMsgs_en[68], "Invalid value for decimal places: %d. It must range from -1 to 40."); - - // File errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[69], "Can't read from table %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[70], "Can't load leaf node!"); - ASSERT2_EQUALS(Sz, errorMsgs_en[71], "Table is corrupted: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_en[72], "Table not closed properly: %s."); // juliana@220_2 - ASSERT2_EQUALS(Sz, errorMsgs_en[73], "A properly closed table can't be used in recoverTable(): %s."); // juliana@222_2 - ASSERT2_EQUALS(Sz, errorMsgs_en[74], "Can't find index record position on delete."); - ASSERT2_EQUALS(Sz, errorMsgs_en[75], "The table format (%d) is incompatible with Litebase version. Please update your tables."); - ASSERT2_EQUALS(Sz, errorMsgs_en[76], "The table format is not the previous one: %s."); // juliana@220_11 - ASSERT2_EQUALS(Sz, errorMsgs_en[77], "Invalid path: %s."); // juliana@214_1 - ASSERT2_EQUALS(Sz, errorMsgs_en[78], "Invalid file position: %d."); - ASSERT2_EQUALS(Sz, errorMsgs_en[79], "Database not found."); - ASSERT2_EQUALS(Sz, errorMsgs_en[80], "An opened table can't be recovered or converted: %s."); // juliana@230_12 - - // BLOB errors. - ASSERT2_EQUALS(Sz, errorMsgs_en[81], "The total size of a blob can't be greater then 10 Mb."); - ASSERT2_EQUALS(Sz, errorMsgs_en[82], "This is not a valid size multiplier."); - ASSERT2_EQUALS(Sz, errorMsgs_en[83], "A blob type can't be part of a primary key."); - ASSERT2_EQUALS(Sz, errorMsgs_en[84], "A BLOB column can't be indexed."); - ASSERT2_EQUALS(Sz, errorMsgs_en[85], "A BLOB can't be in the where clause."); - ASSERT2_EQUALS(Sz, errorMsgs_en[86], "A BLOB can't be converted to a string."); - ASSERT2_EQUALS(Sz, errorMsgs_en[87], "Blobs types can't be in ORDER BY or GROUP BY clauses."); - ASSERT2_EQUALS(Sz, errorMsgs_en[88], "It is not possible to compare BLOBs."); - ASSERT2_EQUALS(Sz, errorMsgs_en[89], "It is only possible to insert or update a BLOB through prepared statements using setBlob()."); - - // Portuguese messages. - // General errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[0], "Erro: "); - ASSERT2_EQUALS(Sz, errorMsgs_pt[1], " Pr�ximo � posi��o %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[2], "Erro de sintaxe."); - - // Limit errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[3], "N�mero m�ximo de campos diferentes foi alcan�ado."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[4], "N�mero m�ximo da lista de par�metros na cl�usula 'WHERE/HAVING' foi alcan�ado."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[5], "Numero m�ximo de �ndices compostos 32 foi alcan�ado."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[6], "Nome da tabela muito grande: deve ser <= 23"); - ASSERT2_EQUALS(Sz, errorMsgs_pt[7], "O n�mero m�ximo de campos na cl�usula SELECT foi excedido."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[8], "O n�mero m�ximo de campos na cl�usula 'ORDER BY/GROUP BY' foi excedido."); - - // Column errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[9], "Coluna desconhecida %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[10], "Nome de coluna inv�lido: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[11], "N�mero de coluna inv�lido: %d."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[12], "A(s) coluna(s) a seguir n�o tem (t�m) um ind�ce associado %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[13], "Nome de coluna amb�guo: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[14], "Coluna n�o encontrada: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[15], "Nome de coluna duplicado: %s."); - - // Primary key errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[16], "Uma chave prim�ria j� foi definida para esta tabela."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[17], "Tabela n�o tem chave prim�ria."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[18], "Comando cria uma chave prim�ria duplicada em %s."); - - // Type errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[19], "Tipos incompativeis."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[20], "Tamanho do campo deve ser um valor inteiro positivo."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[21], "O valor %s n�o � um n�mero v�lido para o tipo desejado: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[22], "Tipo de dados incompat�vel para a chamada de fun��o: %s"); - - // Number of fields errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[23], "O n�mero de campos � diferente do n�mero de valores "); - ASSERT2_EQUALS(Sz, errorMsgs_pt[24], "O n�mero de valores dado n�o coincide com a defini��o da tabela %d."); - - // Default value errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[25], "Tamanho do valor padr�o � maior que o tamanho definido para a coluna."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[26], "Uma coluna adicionada declarada como NOT NULL deve ter um valor padr�o n�o nulo."); - - // Driver errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[27], - "Esta inst�ncia do driver est� fechada e n�o pode ser mais utilizada. Por favor, obtenha uma nova inst�ncia."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[28], "ResultSet j� est� fechado!"); - ASSERT2_EQUALS(Sz, errorMsgs_pt[29], "ResultSetMetaData n�o pode ser usado depois que o ResultSet estiver fechado."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[30], "O id da aplica��o de ter 4 characteres."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[31], "O incremento deve ser maior do que 0 ou -1."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[32], "Iterador j� foi fechado."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[33], "Prepared statement fechado. Por favor, prepare-o novamente."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[34], "Par�metro de conex�o inv�lido: %s."); - - // Table errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[35], "Nome da tabela n�o encontrado: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[36], "Tabela j� existe: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[37], "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de strings diferente."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[38], "N�o � poss�vel abrir uma tabela com uma conex�o com um tipo de criptografia diferente."); - - // ROWID errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[39], "ROWID n�o pode ser mudado pelo usu�rio!"); - - // Prepared Statement errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[40], "Comando SQL n�o retorna um ResultSet."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[41], "Comando SQL n�o executa uma atualiza��o no banco de dados."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[42], "Nem todos os par�metros da consulta tiveram seus valores definidos."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[43], "N�o foi definido um valor para o par�metro %d."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[44], "Invalid parameter index."); - - // Rename errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[45], "N�o � poss�vel renomear a tabela. Esta tabela j� existe: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[46], "Coluna j� existe: %s."); - - // Alias errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[47], "Nome de tabela/alias repetido: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[48], "Este alias j� est� sendo utilizado no sql: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[49], "Um alias � necess�rio para colunas com fun��o de agrega��o."); - - // Litebase.execute() error. - ASSERT2_EQUALS(Sz, errorMsgs_pt[50], "Apenas CREATE TABLE e CREATE INDEX s�o permitidos no Litebase.execute()"); - - // Order by and group by errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[51], "Cl�usulas ORDER BY e GROUP BY devem coincidir."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[52], "SQL com cl�usula GROUP BY n�o tem suporte para colunas virtuais."); - - // Function errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[53], "Todas colunas que n�os�o fun��es de agrega��o na cl�usula SELECT devem estar na cl�usula GROUP BY."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[54], - "%s n�o � uma fun��o de agrega��o. Todos as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[55], "N�o � possivel misturar colunas reais e de agrega��o no SELECT sem cl�usula GROUP BY."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[56], "N�o � poss�vel ter fun��es de agrega��o com cl�usula ORDER BY sem cl�usula GROUP BY."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[57], - "%s n�o foi listado no SELECT. Todas as colunas da cl�usula HAVING devem ser listadas no SELECT utilizando alias."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[58], "Fun��es de agrega��o SUM e AVG n�o s�o usadas com colunas do tipo DATE e DATETIME."); - - // DATE and DATETIME errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[59], "Valor n�o � um tipo DATE v�lido: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[60], "Valor n�o � um tipo DATETIME v�lido: %s."); - - // Index error. - ASSERT2_EQUALS(Sz, errorMsgs_pt[61], "�ndice j� criado para a coluna %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[62], "N�o � poss�vel remover uma chave prim�ria usando drop index."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[63], "�ndice muito grande. Ele n�o pode ter mais do que 65534 n�s."); - - // NOT NULL errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[64], "Chave prim�ria n�o pode ter NULL."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[65], "Coluna n�o pode ser NULL: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[66], "Um par�metro em uma where clause n�o pode ser NULL."); - - // Result set errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[67], "ResultSet em uma posi��o de registro inv�lida %d."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[68], "Valor inv�lido para casas decimais: %d. Deve ficar entre - 1 e 40."); - - // File errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[69], "N�o � poss�vel ler da tabela %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[70], "N�o � poss�vel carregar n� folha!"); - ASSERT2_EQUALS(Sz, errorMsgs_pt[71], "Tabela est� corrompida: %s."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[72], "Tabela n�o foi fechada corretamente: %s."); // juliana@220_2 - ASSERT2_EQUALS(Sz, errorMsgs_pt[73], "Uma tabela fechada corretamente n�o pode ser usada no recoverTable(): %s."); // juliana@222_2 - ASSERT2_EQUALS(Sz, errorMsgs_pt[74], "N�o � poss�vel achar a posi��o de registro no �ndice na exclus�o."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[75], "O formato de tabela (%d) n�o � compat�vel com a vers�o do Litebase. Por favor, atualize suas tabelas."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[76], "O formato de tabela n�o � o anterior: %s."); // juliana@220_11 - ASSERT2_EQUALS(Sz, errorMsgs_pt[77], "Caminho inv�lido: %s."); // juliana@214_1 - ASSERT2_EQUALS(Sz, errorMsgs_pt[78], "Posi��o inv�lida no arquivo: %d."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[79], "Base de dados n�o encontrada."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[80], "Uma tabela aberta n�o pode ser recuperada ou convertida: %s."); // juliana@230_12 - - // BLOB errors. - ASSERT2_EQUALS(Sz, errorMsgs_pt[81], "O tamanho total de um BLOB n�o pode ser maior do que 10 Mb."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[82], "O multiplicador de tamanho n�o � v�lido."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[83], "Um tipo BLOB n�o pode ser parte de uma chave prim�ria."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[84], "Uma coluna do tipo BLOB n�o pode ser indexada."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[85], "Um BLOB n�o pode estar na cl�usula WHERE."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[86], "Um BLOB n�o pode ser convertido em uma string."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[87], "Tipos BLOB n�o podem estar em cl�usulas ORDER BY ou GROUP BY."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[88], "N�o � poss�vel comparar BLOBs."); - ASSERT2_EQUALS(Sz, errorMsgs_pt[89], "S� � poss�vel inserir ou atualizar um BLOB atrav�s prepared statements usando setBlob()."); - -finish : ; -} - -#endif diff --git a/LitebaseSDK/src/native/parser/LitebaseMessage.h b/LitebaseSDK/src/native/parser/LitebaseMessage.h deleted file mode 100644 index a8a5699dfa..0000000000 --- a/LitebaseSDK/src/native/parser/LitebaseMessage.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions used to display Litebase error messages. - */ - -#ifndef LITEBASE_LITEBASEMESSAGE_H -#define LITEBASE_LITEBASEMESSAGE_H - -#include "Litebase.h" - -/** - * Initializes the error message arrays. - */ -void initLitebaseMessage(void); - -/** - * Gets the correct error message. - * - * @param messageNumber The error message code. - * @return The string with the desired error message. - */ -CharP getMessage(int32 messageNumber); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests that getMessage() returns the correct error message. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_getMessage(TestSuite* testSuite, Context currentContext); - -/** - * Tests that initLitebaseMessage() correctly initializes the error messages. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_initLitebaseMessage(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/parser/LitebaseParser.c b/LitebaseSDK/src/native/parser/LitebaseParser.c deleted file mode 100644 index 62dc532d06..0000000000 --- a/LitebaseSDK/src/native/parser/LitebaseParser.c +++ /dev/null @@ -1,2083 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions to initialize, set, and process the parser structure. - */ - -// juliana@253_9: improved Litebase parser. - -#include "LitebaseParser.h" - -/** - * Shows a parser error message without an extra message. - * - * @param error An error code. - * @param parser A pointer to the parser structure. - * @return PARSER_ERROR to indicate that an error has occurred. - * @throws SQLParseException To throw an exception if the error message that occurred. - */ -int32 lbError(int32 error, LitebaseParser* parser) -{ - TRACE("lbError") - char errorMessage[1024]; - xstrcpy(errorMessage, getMessage(ERR_MESSAGE_START)); - xstrcat(errorMessage, getMessage(error)); - xstrcat(errorMessage, getMessage(ERR_MESSAGE_POSITION)); - TC_throwExceptionNamed(parser->context, "litebase.SQLParseException", errorMessage, parser->yyposition); - return PARSER_ERROR; -} - -/** - * Shows a parser error message with an extra message. - * - * @param error An error message. - * @param message An extra error message. - * @param parser A pointer to the parser structure. - * @return PARSER_ERROR to indicate that an error has occurred. - * @throws SQLParseException To throw an exception if the error message that occurred. - */ -int32 lbErrorWithMessage(CharP error, CharP message, LitebaseParser* parser) -{ - TRACE("lbErrorWithMessage") - char errorMessage[1024]; - xstrcpy(errorMessage, getMessage(ERR_MESSAGE_START)); - xstrcat(errorMessage, error); - xstrcat(errorMessage, getMessage(ERR_MESSAGE_POSITION)); - TC_throwExceptionNamed(parser->context, "litebase.SQLParseException", errorMessage, message, parser->yyposition); - return PARSER_ERROR; -} - -/** - * Initializes and parses a sql string. - * - * @param context The thread context where the function is being executed. - * @param sqlStr The sql unicode string. - * @param sqlLen The length of the sql string. - * @param isSelect Indicates if a sql command is a select. - * @param heap A heap to allocate the parser structure. - * @return A pointer to a LitebaseParser structure if the string does not have parser errors or null, otherwise. - */ -LitebaseParser* initLitebaseParser(Context context, JCharP sqlStr, int32 sqlLen, bool isSelect, Heap heap) -{ - TRACE("litebaseParser") - - // The parser only needs to be allocated once per context. - LitebaseParser* parser = context->litebasePtr; - if (parser) - xmemzero(parser, sizeof(LitebaseParser)); - else if (!(parser = context->litebasePtr = (LitebaseParser*)xmalloc(sizeof(LitebaseParser)))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - - // Initializes some parser structures. - parser->heap = heap; - parser->zzReaderChars = sqlStr; - parser->length = sqlLen; - parser->select.type = -1; - parser->isWhereClause = true; - parser->context = context; - parser->yycurrent = ' '; - if (isSelect) - parser->tables = TC_htNew(MAXIMUMS, heap); - - // Does the parsing. - if (yyparse(parser)) - return null; - - return parser; -} - -/** - * Sets the operand type. - * - * @param operandType The type of the operand. - * @param parser A pointer to the parser structure. - * @return A pointer to a SQLBooleanTree structure for a expression tree. - */ -SQLBooleanClauseTree* setOperandType(int32 operandType, LitebaseParser* parser) -{ - TRACE("setOperandType") - SQLBooleanClauseTree* tree = initSQLBooleanClauseTree(getInstanceBooleanClause(parser), parser->heap); - tree->operandType = operandType; - return tree; -} - -/** - * Adds a column field to the order or group by field list. - * - * @param isAscending Indicates if the order by or group by sorting is in ascending or descending order. - * @param isOrderBy Indicates if the field list where to add a list is a order or group by. - * @param parser A pointer to the parser structure, which contains the list(s). - * @return true if the number of fields has not reached its limit; false, otherwise. - */ -bool addColumnFieldOrderGroupBy(bool isAscending, bool isOrderBy, LitebaseParser* parser) -{ - TRACE("addColumnFieldOrderGroupBy") - int32 hash; - SQLColumnListClause* listClause = isOrderBy? &parser->orderBy : &parser->groupBy; - SQLResultSetField** fieldList = isOrderBy? parser->orderByfieldList : parser->groupByfieldList; - SQLResultSetField* field = parser->auxField; - - // The number of fields has reached the maximum. - if (listClause->fieldsCount == MAXIMUMS) - { - lbError(ERR_FIELD_OVERFLOW_GROUPBY_ORDERBY, parser); - return false; - } - - // Sets the field. - field->tableColHashCode = hash = TC_hashCode(field->tableColName); - field->aliasHashCode = hash; - field->isAscending = isAscending; - fieldList[listClause->fieldsCount++] = field; - return true; -} - -/** - * Gets a especific boolean clause: it will get a where clause if there is a where clause; otherwise, it will get a having clause. - * - * @param parser A pointer to the parser structure. - * @return A pointer to a SQLBooleanClause. - */ -SQLBooleanClause* getInstanceBooleanClause(LitebaseParser* parser) -{ - TRACE("getInstanceBooleanClause") - if (parser->isWhereClause) // It is a where clause. - { - if (!parser->whereClause) - parser->whereClause = initSQLBooleanClause(parser->heap); - return parser->whereClause; - } - if (!parser->havingClause) // It is a having clause. - parser->havingClause = initSQLBooleanClause(parser->heap); - return parser->havingClause; -} - -/** - * Initializes a field definition for a table being created. - * - * @param fieldName The name of the new field. - * @param fieldType The type of the new field. - * @param fieldSize The size of the new field (it is zero if the type is not CHARS or CHARS_NOCASE). - * @param isPrimaryKey Indicates if the new field is the primary key. - * @param defaultValue The default value of the new field (it can be null if there is no default value). - * @param isNotNull Indicates if the new field can be null or not. - * @return a pointer to the new result set table structure. - */ -SQLFieldDefinition* initSQLFieldDefinition(CharP fieldName, int32 fieldType, int32 fieldSize, bool isPrimaryKey, JCharP defaultValue, bool isNotNull, - Heap heap) -{ - TRACE("initSQLFieldDefinition") - SQLFieldDefinition *sqlFieldDefinition = (SQLFieldDefinition*)TC_heapAlloc(heap, sizeof(SQLFieldDefinition)); - sqlFieldDefinition->fieldName = fieldName; - sqlFieldDefinition->fieldType = fieldType; - sqlFieldDefinition->fieldSize = fieldSize; - sqlFieldDefinition->isPrimaryKey = isPrimaryKey; - sqlFieldDefinition->defaultValue = defaultValue; - sqlFieldDefinition->isNotNull = isNotNull; - return sqlFieldDefinition; -} - -/** - * Initializes a result set field. - * - * @param heap A heap to alocate the result set field structure. - * @return A pointer to a result set field structure. - */ -SQLResultSetField* initSQLResultSetField(Heap heap) -{ - TRACE("initSQLResultSetField") - SQLResultSetField* sqlResultSetField = (SQLResultSetField*)TC_heapAlloc(heap, sizeof(SQLResultSetField)); - sqlResultSetField->tableColIndex = -1; - sqlResultSetField->dataType = UNDEFINED_TYPE; - sqlResultSetField->sqlFunction = FUNCTION_AGG_NONE; - sqlResultSetField->isAscending = 1; - return sqlResultSetField; -} - -/** - * Initializes a result set table structure using a table name and its optional alias name. - * - * @param tableName the name of the new table. - * @param aliasTableName the optional alias name of the new table (it can be null). - * @return a pointer to the new result set table structure. - */ -SQLResultSetTable* initSQLResultSetTable(CharP tableName, CharP aliasTableName, Heap heap) -{ - TRACE("initSQLResultSetTable") - - // Allocates the table structure. - SQLResultSetTable* sqlResultSetTable = (SQLResultSetTable*)TC_heapAlloc(heap, sizeof(SQLResultSetTable)); - - sqlResultSetTable->tableName = tableName; // Sets the table name. - - // If the alias is null, it receives the name of the table. If the alias is not null, the alias name is set with the alias passed as a parameter. - sqlResultSetTable->aliasTableNameHashCode = TC_hashCode((sqlResultSetTable->aliasTableName = (!aliasTableName) ? tableName : aliasTableName)); - - return sqlResultSetTable; -} - -/** - * The function that parses the sql string. - * - * @param parser The parser structure. - * @return true if there are parser errors; false, otherwise. - */ -bool yyparse(LitebaseParser* parser) -{ - TRACE("yyparse") - int32 token = PARSER_EOF; - CharP tableName; - - switch (yylex(parser)) - { - case TK_ALTER: // Alter table. - if (yylex(parser) != TK_TABLE || yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->tableList[0] = initSQLResultSetTable(parser->yylval, null, parser->heap); // There's no alias table name here. - - switch (yylex(parser)) - { - case TK_ADD: // Adds a primary key or a new column. - - // juliana@253_22: added command ALTER TABLE ADD column. - if ((token = createColumn(parser)) == TK_PRIMARY) // Adds a primary key. - { - if (parser->fieldListSize == 1) - return lbError(ERR_SYNTAX_ERROR, parser); - if (yylex(parser) != TK_KEY || yylex(parser) != TK_OPEN || colnameCommaList(parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->command = CMD_ALTER_ADD_PK; - } - else if (token == -1) // Adds a new column. - { - SQLFieldDefinition* field = parser->fieldList[0]; - if (field->isNotNull && !field->defaultValue) // A field declared as not null must have a default value. - return lbError(ERR_NOT_NULL_DEFAULT, parser); - if (field->isPrimaryKey) // The new field can't be declared as a primary key when being added. - { - if (field->defaultValue) // All the keys would be the same. - return lbErrorWithMessage(getMessage(ERR_STATEMENT_CREATE_DUPLICATED_PK), parser->tableList[0]->tableName, parser); - return lbError(ERR_PK_CANT_BE_NULL, parser); // All the keys would be null. - } - parser->command = CMD_ALTER_ADD_COLUMN; - } - break; - - case TK_DROP: // Drops a primary key. - if (yylex(parser) != TK_PRIMARY || yylex(parser) != TK_KEY) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->command = CMD_ALTER_DROP_PK; - break; - - case TK_RENAME: // Renames the table or a column. - if ((token = yylex(parser)) == TK_IDENT) // Rename column. - { - parser->command = CMD_ALTER_RENAME_COLUMN; - parser->fieldNames[1] = parser->yylval; - token = yylex(parser); - } - else // Rename table. - parser->command = CMD_ALTER_RENAME_TABLE; - - // New name. - if (token != TK_TO || yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->fieldNames[0] = parser->yylval; - - break; - - default: - return lbError(ERR_SYNTAX_ERROR, parser); - } - - token = yylex(parser); - break; - - case TK_CREATE: - { - switch (yylex(parser)) - { - case TK_TABLE: // Create table. - if (yylex(parser) != TK_IDENT || yylex(parser) != TK_OPEN) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->tableList[0] = initSQLResultSetTable(parser->yylval, null, parser->heap); // There's no alias table name here. - - // Primary key. - if ((token = createColumnCommalist(parser)) == TK_PRIMARY && yylex(parser) == TK_KEY && yylex(parser) == TK_OPEN && colnameCommaList(parser) == TK_CLOSE) - { - if (parser->numberPK == 1) - return lbError(ERR_PRIMARY_KEY_ALREADY_DEFINED, parser); - token = yylex(parser); - } - - if (token != TK_CLOSE) // End of create table. - return lbError(ERR_SYNTAX_ERROR, parser); - - parser->command = CMD_CREATE_TABLE; - break; - - case TK_INDEX: // Create index. - if (yylex(parser) != TK_IDENT || yylex(parser) != TK_ON || yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->tableList[0] = initSQLResultSetTable(parser->yylval, null, parser->heap); // There's no alias table name here. - if (yylex(parser) != TK_OPEN || colnameCommaList(parser) != TK_CLOSE) // Column name list. - return lbError(ERR_SYNTAX_ERROR, parser); - parser->command = CMD_CREATE_INDEX; - break; - - default: - return lbError(ERR_SYNTAX_ERROR, parser); - } - - token = yylex(parser); - break; - } - case TK_DELETE: // Delete. - if (!(((token = yylex(parser)) == TK_FROM && yylex(parser) == TK_IDENT) || token == TK_IDENT)) - return lbError(ERR_SYNTAX_ERROR, parser); - tableName = parser->yylval; - - if ((token = yylex(parser)) == TK_IDENT) // Alias table name. - { - parser->tableList[0] = initSQLResultSetTable(tableName, parser->yylval, parser->heap); - token = yylex(parser); - } - else - parser->tableList[0] = initSQLResultSetTable(tableName, null, parser->heap); - - token = optWhereClause(token, parser); // Where clause. - parser->command = CMD_DELETE; - break; - - case TK_DROP: - switch (yylex(parser)) - { - case TK_TABLE: // Drop table. - if (yylex(parser) == TK_IDENT) - parser->tableList[0] = initSQLResultSetTable(parser->yylval, null, parser->heap); // There's no alias table name here. - else - return lbError(ERR_SYNTAX_ERROR, parser); - parser->command = CMD_DROP_TABLE; - break; - - case TK_INDEX: // Drop index. - if ((token = colnameCommaList(parser)) != TK_ON || yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->tableList[0] = initSQLResultSetTable(parser->yylval, null, parser->heap); // There's no alias table name here. - parser->command = CMD_DROP_INDEX; - break; - - default: - return lbError(ERR_SYNTAX_ERROR, parser); - } - - token = yylex(parser); - break; - - case TK_INSERT: // Insert. - if (yylex(parser) != TK_INTO || yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->tableList[0] = initSQLResultSetTable(parser->yylval, null, parser->heap); // There's no alias table name here. - - if ((token = yylex(parser)) == TK_OPEN) // Reads the field list. - { - if (colnameCommaList(parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - token = yylex(parser); - } - - if (token != TK_VALUES || yylex(parser) != TK_OPEN || listValues(parser) != TK_CLOSE) // Reads the value list. - return lbError(ERR_SYNTAX_ERROR, parser); - - // If the default order is not used, the number of values must be equal to the number of fields. - if (parser->fieldNamesSize && parser->fieldNamesSize != parser->fieldValuesSize) - { - char error[MAXIMUMS]; - xstrprintf(error, "%s (%d != %d) ", getMessage(ERR_NUMBER_FIELDS_AND_VALUES_DOES_NOT_MATCH), parserTP->fieldNamesSize, - parserTP->fieldValuesSize); - xstrcat(error, "%s ."); - return lbErrorWithMessage(error, "", parser); - } - parser->command = CMD_INSERT; - token = yylex(parser); - break; - - case TK_SELECT: // Select. - if ((token = yylex(parser)) == TK_DISTINCT) - token = yylex(parser); - if (fieldExp(token, parser) != TK_FROM) - return lbError(ERR_SYNTAX_ERROR, parser); - - token = optWhereClause(tableList(parser), parser); // Table list and where clause. - - // order by and group by. - if (token == TK_GROUP) - { - if (yylex(parser) != TK_BY) - return lbError(ERR_SYNTAX_ERROR, parser); - token = groupByClause(parser); - } - if (token == TK_ORDER) - { - if (yylex(parser) != TK_BY) - return lbError(ERR_SYNTAX_ERROR, parser); - token = orderByClause(parser); - } - - parser->command = CMD_SELECT; - - // Checks if the first field is the wild card. If so, assigns null to list, to indicate that all fields must be included. - if (parser->selectFieldList[0]->isWildcard) - parser->select.fieldsCount = 0; - break; - - case TK_UPDATE: // Update. - { - CharP tableAlias = null; - - // Table name. - if (yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - tableAlias = tableName = parser->yylval; - - if ((token = yylex(parser)) == TK_IDENT) // Alias table name. - { - tableAlias = parser->yylval; - token = yylex(parser); - } - - if (token != TK_SET) // set key word. - return lbError(ERR_SYNTAX_ERROR, parser); - - token = optWhereClause(updateExpCommalist(parser), parser); // Update expression list and where clause. - - if (parser->secondFieldUpdateTableName) // Verifies if there was an error on field.tableName. - return lbErrorWithMessage(getMessage(ERR_INVALID_COLUMN_NAME), xstrcmp(tableName, parser->firstFieldUpdateTableName)? - parser->firstFieldUpdateAlias : parser->secondFieldUpdateAlias, parser); - else if (parser->firstFieldUpdateTableName && xstrcmp(tableAlias, parser->firstFieldUpdateTableName)) - return lbErrorWithMessage(getMessage(ERR_INVALID_COLUMN_NAME), parser->firstFieldUpdateAlias, parser); - - parser->command = CMD_UPDATE; - parser->tableList[0] = initSQLResultSetTable(tableName, tableAlias, parser->heap); - break; - } - - default: - return lbError(ERR_SYNTAX_ERROR, parser); - } - - if (token != PARSER_EOF) - return lbError(ERR_SYNTAX_ERROR, parser); - return false; -} - -/** - * Deals with a list of identifiers separated by commas. - * - * @param parser The parser structure. - * @return The token after the list of identifiers. - */ -int32 colnameCommaList(LitebaseParser* parser) -{ - int32 token, - fieldNamesSize = parser->fieldNamesSize; - CharP* fieldNames = parser->fieldNames; - - do - { - if ((token = yylex(parser)) == TK_ASTERISK) // This is necessary for dropping all indices. - { - fieldNames[fieldNamesSize++] = "*"; - return yylex(parser); - } - - if (token != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - if (fieldNamesSize == MAXIMUMS) - return lbError(ERR_MAX_NUM_FIELDS_REACHED, parser); - fieldNames[fieldNamesSize++] = parser->yylval; // Adds the column name. - } - while ((token = yylex(parser)) == TK_COMMA); - parser->fieldNamesSize = fieldNamesSize; - return token; -} - -/** - * Deals with a list of rows of a table being created. - * - * @param parser The parser structure. - * @return The token after the list of rows. - */ -int32 createColumnCommalist(LitebaseParser* parser) -{ - int32 token; - - while ((token = createColumn(parser)) == TK_COMMA); - if (!parser->fieldListSize) // The number of columns can't be zero. - return lbError(ERR_SYNTAX_ERROR, parser); - - return token; -} - -/** - * Deals with a column declaration. - * - * @param parser The parser structure. - * @return The token after a column declaration. - */ -int32 createColumn(LitebaseParser* parser) -{ - int32 token, - type, - size = 0; - bool isPrimaryKey = false, - isNotNull = false; - CharP columnName; - JCharP strDefault = null; - - if ((token = yylex(parser)) == TK_PRIMARY) // The next token after ',' is a primary key declaration. This is not treated here. - return token; - - // Column name. - if (token != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - columnName = parser->yylval; - - // Column type. - if ((type = yylex(parser)) == BOOLEAN_TYPE || type > BLOB_TYPE || type < CHARS_TYPE) - return lbError(ERR_SYNTAX_ERROR, parser); - if (type == TK_VARCHAR) - type = CHARS_TYPE; - - if (type == CHARS_TYPE || type == BLOB_TYPE) // Size and multiplier. - { - if (yylex(parser) == TK_OPEN && yylex(parser) == TK_NUMBER) - { - bool error; - IntBuf buffer; - if ((size = TC_str2int(TC_JCharP2CharPBuf((JCharP)parser->yylval, -1, buffer), &error)) <= 0 || error) - return lbError(ERR_FIELD_SIZE_IS_NOT_INT, parser); - } - else - return lbError(ERR_SYNTAX_ERROR, parser); - - if (type == CHARS_TYPE && yylex(parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - else if (type == BLOB_TYPE) - { - if ((token = yylex(parser)) != TK_IDENT && token != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - if (token == TK_IDENT) - { - CharP multiplier = (CharP)parser->yylval; - if (multiplier[0] == 'k' && !multiplier[1]) // kilobytes. - size <<= 10; - else if (multiplier[0] == 'm' && !multiplier[1]) // megabytes. - size <<= 20; - else - return lbError(ERR_INVALID_MULTIPLIER, parser); - if (yylex(parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - } - if (size > (10 << 20)) // There is a size limit for a blob! - return lbError(ERR_BLOB_TOO_BIG, parser); - } - - // juliana@253_15: now an exception is thrown if the size of a CHAR or VARCHAR is greater than 65535. - else if (type == CHARS_TYPE && size > (MAX_SHORT_VALUE << 1) + 1) - return lbErrorWithMessage(getMessage(ERR_INVALID_NUMBER), "unsigned short", parser); - } - - if ((token = yylex(parser)) == TK_NOCASE) // No case. - { - if (type == CHARS_TYPE) - type = CHARS_NOCASE_TYPE; - else - return lbError(ERR_SYNTAX_ERROR, parser); - token = yylex(parser); - } - - if (token == TK_PRIMARY) // Simple primary key. - { - if (yylex(parser) != TK_KEY) - return lbError(ERR_SYNTAX_ERROR, parser); - if (parser->numberPK++ == 1) - return lbError(ERR_PRIMARY_KEY_ALREADY_DEFINED, parser); - if (type == BLOB_TYPE) - return lbError(ERR_BLOB_PRIMARY_KEY, parser); - token = yylex(parser); - isPrimaryKey = true; - } - - if (token == TK_DEFAULT) // Default value. - { - if ((token = yylex(parser)) == TK_NUMBER || token == TK_STR) - strDefault = parser->yylval; - else if (token != TK_NULL) - return lbError(ERR_SYNTAX_ERROR, parser); - - if (type == BLOB_TYPE) // A blob can't have a default value. - return lbError(ERR_BLOB_STRING, parser); - - // A numeric type must have a number as a default value. A string, date or datetime type must have a string as a default value. - if (((type == CHARS_TYPE || type == CHARS_NOCASE_TYPE || type == DATE_TYPE || type == DATETIME_TYPE) && token == TK_NUMBER) - || ((type > CHARS_TYPE && type < CHARS_NOCASE_TYPE) && token == TK_STR)) - return lbError(ERR_SYNTAX_ERROR, parser); - - token = yylex(parser); - } - - if (token == TK_NOT) // Not null. - { - if (yylex(parser) != TK_NULL) - return lbError(ERR_SYNTAX_ERROR, parser); - token = yylex(parser); - isNotNull = true; - } - if (parser->fieldListSize == MAXIMUMS) - return lbError(ERR_MAX_NUM_FIELDS_REACHED, parser); - parser->fieldList[parser->fieldListSize++] = initSQLFieldDefinition(columnName, type, size, isPrimaryKey, strDefault, isNotNull, parser->heap); - return token; -} - -/** - * Deals with an expression of an expression tree of a where clause. - * - * @param token The first token of the expression. - * @param parser The parser structure. - * @return The token after the expression. - */ -int32 expression(int32 token, LitebaseParser* parser) -{ - if ((token = term(token, parser)) == TK_OR) // expression = term or expression | term - { - // juliana@213_1: changed the way a tree with ORs is built in order to speed up queries with indices. - SQLBooleanClauseTree* tree = setOperandType(OP_BOOLEAN_OR, parser); - (tree->rightTree = parser->auxTree)->parent = tree; - token = expression(yylex(parser), parser); - (tree->leftTree = parser->auxTree)->parent = tree; - parser->auxTree = tree; - } - - return token; -} - -/** - * Deals with a term of an expression tree of a where clause. - * - * @param token The first token of the term. - * @param parser The parser structure. - * @return The token after the term. - */ -int32 term(int32 token, LitebaseParser* parser) -{ - if ((token = factor(token, parser)) == TK_AND) // term = factor or factor | term - { - SQLBooleanClauseTree* tree = setOperandType(OP_BOOLEAN_AND, parser); - (tree->rightTree = parser->auxTree)->parent = tree; - token = term(yylex(parser), parser); - (tree->leftTree = parser->auxTree)->parent = tree; - parser->auxTree = tree; - } - - return token; -} - -/** - * Deals with a factor of an expression tree of a where clause. - * - * @param token The first token of the factor. - * @param parser The parser structure. - * @return The token after the factor. - */ -int32 factor(int32 token, LitebaseParser* parser) -{ - SQLBooleanClauseTree* tree = null; - SQLBooleanClauseTree* rightTree; - SQLBooleanClause* booleanClause; - - if (token == TK_OPEN) // factor = (expression) - { - if ((token = expression(yylex(parser), parser)) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - return yylex(parser); - } - - if (token == TK_NOT) - { - if ((token = yylex(parser)) == TK_OPEN) // factor = not (expression) - { - if ((token = expression(yylex(parser), parser)) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - token = yylex(parser); - } - else // fator = not factor - token = factor(token, parser); - - // The parent node will be the negation operator and the expression will be the right tree. - tree = setOperandType(OP_BOOLEAN_NOT, parser); - (tree->rightTree = parser->auxTree)->parent = tree; - parser->auxTree = tree; - - return token; - } - - // factor = single expression (< | > | = | <> | != | <= | >=) single expression - if ((token = singleExp(token, parser)) == TK_EQUAL || token == TK_LESS || token == TK_DIFF || token == TK_GREATER || token == TK_GREATER_EQUAL - || token == TK_LESS_EQUAL) - { - switch (token) - { - case TK_LESS: - tree = setOperandType(OP_REL_LESS, parser); - break; - case TK_EQUAL: - tree = setOperandType(OP_REL_EQUAL, parser); - break; - case TK_GREATER: - tree = setOperandType(OP_REL_GREATER, parser); - break; - case TK_GREATER_EQUAL: - tree = setOperandType(OP_REL_GREATER_EQUAL, parser); - break; - case TK_LESS_EQUAL: - tree = setOperandType(OP_REL_LESS_EQUAL, parser); - break; - case TK_DIFF: - tree = setOperandType(OP_REL_DIFF, parser); - } - (tree->leftTree = parser->auxTree)->parent = tree; - token = singleExp(yylex(parser), parser); - (tree->rightTree = parser->auxTree)->parent = tree; - parser->auxTree = tree; - return token; - } - - if (token == TK_IS) // factor = single expression is [not] null. - { - if ((token = yylex(parser)) == TK_NOT) - { - tree = setOperandType(OP_PAT_IS_NOT, parser); - token = yylex(parser); - } - else - tree = setOperandType(OP_PAT_IS, parser); - if (token != TK_NULL) - return lbError(ERR_SYNTAX_ERROR, parser); - - (tree->rightTree = setOperandType(OP_PAT_NULL, parser))->parent = (tree->leftTree = parser->auxTree)->parent = tree; - parser->auxTree = tree; - - return yylex(parser); - } - - if (token == TK_NOT) // factor = single expression not like [string | ?] - { - token = yylex(parser); - tree = setOperandType(OP_PAT_MATCH_NOT_LIKE, parser); - } - else // factor = single expression like [string | ?] - tree = setOperandType(OP_PAT_MATCH_LIKE, parser); - - booleanClause = getInstanceBooleanClause(parser); - rightTree = initSQLBooleanClauseTree(booleanClause, parser->heap); - - if (token == TK_LIKE) - { - if ((token = yylex(parser)) == TK_STR) // string - setOperandStringLiteral(rightTree, parser->yylval); - else if (token == TK_INTERROGATION) // ? - { - if (booleanClause->paramCount == MAXIMUMS) // There is a maximum number of parameters. - return lbError(ERR_MAX_NUM_PARAMS_REACHED, parser); - rightTree->isParameter = true; - if (parser->isWhereClause) - parser->whereParamList[booleanClause->paramCount++] = rightTree; - else - parser->havingParamList[booleanClause->paramCount++] = rightTree; - } - else - return lbError(ERR_SYNTAX_ERROR, parser); - - (tree->rightTree = rightTree)->parent = (tree->leftTree = parser->auxTree)->parent = tree; - parser->auxTree = tree; - - return yylex(parser); - } - - return lbError(ERR_SYNTAX_ERROR, parser); -} - -/** - * Deals with a single expression of an expression tree of a where clause. - * - * @param token The first token of the single expression. - * @param parser The parser structure. - * @return The token after the single expression. - */ -int32 singleExp(int32 token, LitebaseParser* parser) -{ - int32 auxToken; - SQLBooleanClauseTree* tree; - - if (token == TK_NUMBER) // single expression = number - { - // juliana@226a_20 - (tree = parser->auxTree = initSQLBooleanClauseTree(getInstanceBooleanClause(parser), parser->heap))->operandValue.asChars = parser->yylval; - return yylex(parser); - } - else if (token == TK_STR) // single expression = string - { - setOperandStringLiteral((tree = parser->auxTree = initSQLBooleanClauseTree(getInstanceBooleanClause(parser), parser->heap)), (JCharP)parser->yylval); - return yylex(parser); - } - else if (token == TK_INTERROGATION) // single expression = ? - { - SQLBooleanClause* booleanClause = getInstanceBooleanClause(parser); - - if (booleanClause->paramCount == MAXIMUMS) // There is a maximum number of parameters. - return lbError(ERR_MAX_NUM_PARAMS_REACHED, parser); - - if (parser->isWhereClause) - (parser->whereParamList[booleanClause->paramCount++] = parser->auxTree = initSQLBooleanClauseTree(booleanClause, parser->heap))->isParameter = true; - else - (parser->havingParamList[booleanClause->paramCount++] = parser->auxTree = initSQLBooleanClauseTree(booleanClause, parser->heap))->isParameter = true; - return yylex(parser); - } - else if ((auxToken = dataFunction(token, parser)) != -1) // single expression = function(...) - { - SQLBooleanClause* booleanClause = getInstanceBooleanClause(parser); - int32 i = 1, - index = booleanClause->fieldsCount, - hashCode; - SQLResultSetField* field = parser->auxField; - Hashtable* fieldName2Index = &booleanClause->fieldName2Index; - SQLResultSetField* paramField = field->parameter = initSQLResultSetField(parser->heap); // Creates the parameter field. - - (parser->auxTree = tree = initSQLBooleanClauseTree(booleanClause, parser->heap))->operandType = OP_IDENTIFIER; - hashCode = tree->nameSqlFunctionHashCode = tree->nameHashCode = TC_hashCode((tree->operandName = field->tableColName)); - - // generates different indexes to repeted columns on where clause. - // Ex: where year(birth) = 2000 and day(birth) = 3. - while (TC_htGet32Inv(fieldName2Index, tree->nameSqlFunctionHashCode) >= 0) - tree->nameSqlFunctionHashCode = (hashCode << 5) - hashCode + i++ - 48; - - if (index == MAXIMUMS) // There is a maximum number of columns. - return lbError(ERR_MAX_NUM_FIELDS_REACHED, parser); - - // Puts the hash code of the function name in the hash table. - TC_htPut32(fieldName2Index, tree->nameSqlFunctionHashCode, index); - - // Sets the field and function parameter fields. - paramField->alias = paramField->tableColName = field->alias = field->tableColName = tree->operandName; - paramField->aliasHashCode = paramField->tableColHashCode = field->tableColHashCode = field->aliasHashCode = tree->nameHashCode; - field->dataType = dataTypeFunctionsTypes[field->sqlFunction]; - field->isDataTypeFunction = field->isVirtual = true; - - // Puts the field in the field list. - if (parser->isWhereClause) - parser->whereFieldList[booleanClause->fieldsCount++] = field; - else - parser->havingFieldList[booleanClause->fieldsCount++] = field; - - return auxToken; - } - else if (token != TK_NULL)// single expression = pure field. - { - SQLBooleanClause* booleanClause = getInstanceBooleanClause(parser); - int32 i = 1, - index = booleanClause->fieldsCount, - hashCode; - SQLResultSetField* field; - Hashtable* fieldName2Index = &booleanClause->fieldName2Index; - - token = pureField(token, parser); - field = parser->auxField; - - (parser->auxTree = tree = initSQLBooleanClauseTree(booleanClause, parser->heap))->operandType = OP_IDENTIFIER; - hashCode = field->tableColHashCode = tree->nameSqlFunctionHashCode = tree->nameHashCode - = TC_hashCode((tree->operandName = field->tableColName)); - - // rnovais@570_108: Generates different index to repeted columns on where - // clause. Ex: where year(birth) = 2000 and birth = '2008/02/11'. - while (TC_htGet32Inv(fieldName2Index, tree->nameSqlFunctionHashCode) >= 0) - tree->nameSqlFunctionHashCode = (hashCode << 5) - hashCode + i++ - 48; - - if (index == MAXIMUMS) // There is a maximum number of columns. - return lbError(ERR_MAX_NUM_FIELDS_REACHED, parser); - - // Puts the hash code of the function name in the hash table. - TC_htPut32(fieldName2Index, tree->nameSqlFunctionHashCode, index); - - field->aliasHashCode = TC_hashCode(field->alias); // Sets the hash code of the field alias. - - // Puts the field in the field list. - if (parser->isWhereClause) - parser->whereFieldList[booleanClause->fieldsCount++] = field; - else - parser->havingFieldList[booleanClause->fieldsCount++] = field; - - return token; - } - return lbError(ERR_SYNTAX_ERROR, parser); -} - -/** - * Deals with a list of values of an insert. - * - * @param parser The parser structure. - * @return The token after the list of values. - */ -int32 listValues(LitebaseParser* parser) -{ - int32 token, - size = 0; - JCharP* values = parser->fieldValues; - - do - switch (token = yylex(parser)) - { - case TK_NULL: // Null. - size++; - break; - case TK_INTERROGATION: // A variable for prepared statements. - values[size++] = questionMark; - break; - case TK_NUMBER: // A number. - case TK_STR: // A string. - values[size++] = (JCharP)parser->yylval; - break; - default: // The list of values is finished or an error occurred. - { - if (!size) // There must be a value to be inserted. - return lbError(ERR_SYNTAX_ERROR, parser); - return token; - } - } - while ((token = yylex(parser)) == TK_COMMA); - parser->fieldValuesSize = size; - return token; -} - -/** - * Deals with a table list of a select. - * - * @param parser The parser structure. - * @return The token after the list of tables. - */ -int32 tableList(LitebaseParser* parser) -{ - int32 token, - size = 0, - hash; - CharP tableName, - tableAlias; - Hashtable* tables = &parser->tables; - SQLResultSetTable** list = parser->tableList; - - do - { - if ((token = yylex(parser)) != TK_IDENT) // Not a table name, return. - { - if (!(parser->select.tableListSize = size)) // There must be at least a table. - return lbError(ERR_SYNTAX_ERROR, parser); - return token; - } - tableName = tableAlias = (CharP)parser->yylval; // Table name. - - // Table alias. - if ((token = yylex(parser)) == TK_AS) - token = yylex(parser); - if (token == TK_IDENT) - { - tableAlias = (CharP)parser->yylval; - token = yylex(parser); - } - - // The table name alias must be unique. - if (TC_htGet32Inv(&parserTP->tables, (hash = TC_hashCode(tableAlias))) != -1) - return lbErrorWithMessage(getMessage(ERR_NOT_UNIQUE_ALIAS_TABLE), tableAlias, parser); - else - TC_htPut32(tables, hash, size); - - list[size++] = initSQLResultSetTable(tableName, tableAlias, parser->heap); - } - while (token == TK_COMMA); - parser->select.tableListSize = size; - return token; -} - -/** - * Deals with a list of expressions of a select. - * - * @param parser The parser structure. - * @return The token after the list of expressions. - */ -int32 fieldExp(int32 token, LitebaseParser* parser) -{ - SQLSelectClause* select = &parser->select; - SQLResultSetField** resultFieldList = parser->selectFieldList; - int32 i; - - if (token == TK_ASTERISK) // All fields. - { - // Adds a wildcard field. - (resultFieldList[select->fieldsCount++] = initSQLResultSetField(parser->heap))->isWildcard = true; - token = yylex(parser); - } - else if (token != PARSER_ERROR) - { - CharP alias = null; - - do - { - if (token == TK_COMMA) // Gets the next field list token. - token = yylex(parser); - - if ((token = field(token, parser)) == TK_AS) // There is an alias. - { - if (yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - alias = (CharP)parser->yylval; - token = yylex(parser); - } - else if (token != PARSER_ERROR) - { - // If the alias_name is null, the alias must be the name of the column. This was already done before. - // If the alias is null and the field is a virtual column, raises an exception, since virtual columns require explicit aliases. - if (parser->auxField->isVirtual) - return lbError(ERR_REQUIRED_ALIAS, parser); - - alias = parser->auxField->alias; // The null alias name is filled as tableColName or tableName.tableColName, which was set before. - } - else - return PARSER_ERROR; - - // Checks if the alias has not already been used by a predecessor. - i = select->fieldsCount - 1; - - while (--i >= 0) - if (strEq(resultFieldList[i]->alias, alias)) - return lbErrorWithMessage(getMessage(ERR_DUPLICATE_ALIAS), alias, parser); - - parser->auxField->aliasHashCode = TC_hashCode(parser->auxField->alias = alias); // Assigns the alias. - } - while (token == TK_COMMA); - } - return token; -} - -/** - * Deals with a list of update expressions. - * - * @param parser The parser structure. - * @return The token after the list of update expressions. - */ -int32 updateExpCommalist(LitebaseParser* parser) -{ - int32 token, - size = 0; - JCharP* values = parser->fieldValues; - CharP* names = parser->fieldNames; - SQLResultSetField* field; - - do - { - if (pureField(yylex(parser), parser) != TK_EQUAL) // field being updated. - return lbError(ERR_SYNTAX_ERROR, parser); - field = parser->auxField; - - // New value. - if ((token = yylex(parser)) == TK_STR || token == TK_NUMBER) // A string or a number. - values[size++] = (JCharP)parser->yylval; - else if (token == TK_NULL) // null - size++; - else if (token == TK_INTERROGATION) // A prepared statement parameter. - values[size++] = questionMark; - else - return lbError(ERR_SYNTAX_ERROR, parser); - - if (!parser->firstFieldUpdateTableName) // After the table name verification, the associated table name on the field name is discarded. - { - if (field->tableName) - { - parser->firstFieldUpdateTableName = field->tableName; - parser->firstFieldUpdateAlias = field->alias; - } - } - else if (xstrcmp(field->tableName, parser->firstFieldUpdateTableName)) - - // Verifies if it is different. - // There is an error: update has just one table. This error will raise an exception later on. - { - parser->secondFieldUpdateTableName = field->tableName; - parser->secondFieldUpdateAlias = field->alias; - } - names[size - 1] = field->tableColName; - } - while ((token = yylex(parser)) == TK_COMMA); - if (!(parser->fieldNamesSize = parser->fieldValuesSize = size)) - return lbError(ERR_SYNTAX_ERROR, parser); - return token; -} - -/** - * Deals with a field. - * - * @param token A token to be used by the field. - * @param parser The parser structure. - * @return The token after the field. - */ -int32 field(int32 token, LitebaseParser* parser) -{ - int32 tokenAux; - SQLSelectClause* select = &parser->select; - SQLResultSetField* field = null; - - if (token == TK_IDENT) // A pure field. - { - token = pureField(token, parser); - - if (select->fieldsCount == MAXIMUMS) // The maximum number of fields can't be reached. - return lbError(ERR_FIELDS_OVERFLOW, parser); - - parser->selectFieldList[select->fieldsCount++] = field = parser->auxField; - field->tableColHashCode = TC_hashCode(field->tableColName); - select->hasRealColumns = true; - tokenAux = token; - } - else - { - if ((tokenAux = dataFunction(token, parser)) >= 0) // A function applied to a field. - { - SQLResultSetField* paramField = (field = parser->auxField)->parameter = initSQLResultSetField(parser->heap); - - // Sets the field. - field->isDataTypeFunction = field->isVirtual = true; - field->dataType = dataTypeFunctionsTypes[field->sqlFunction]; - - // Sets the function parameter. - paramField->alias = paramField->tableColName = field->tableColName; - paramField->tableColHashCode = TC_hashCode(paramField->tableColName); - field->tableColHashCode = paramField->aliasHashCode = paramField->tableColHashCode; - } - else if ((tokenAux = aggFunction(token, parser)) >= 0) // An aggregation function applied to a field. - { - // Sets the field. - field = parser->auxField; - field->isAggregatedFunction = field->isVirtual = true; - field->dataType = aggregateFunctionsTypes[field->sqlFunction]; - - // Sets the parameter, if there is such one. - if (field->sqlFunction != FUNCTION_AGG_COUNT) - { - // Sets the function parameter. - SQLResultSetField* paramField = field->parameter = initSQLResultSetField(parser->heap); - paramField->alias = paramField->tableColName = field->tableColName; - paramField->tableColHashCode = TC_hashCode(paramField->tableColName); - field->tableColHashCode = paramField->aliasHashCode = paramField->tableColHashCode; - } - - select->hasAggFunctions = true; - } - else - return lbError(ERR_SYNTAX_ERROR, parser); - - if (select->fieldsCount == MAXIMUMS) // The maximum number of fields can't be reached. - return lbError(ERR_FIELDS_OVERFLOW, parser); - parser->selectFieldList[select->fieldsCount++] = field; // Sets the select statement. - } - - return tokenAux; -} - -/** - * Deals with a pure field. - * - * @param token A token to be used by the pure field. - * @param parser The parser structure. - * @return The token after the pure field. - */ -int32 pureField(int32 token, LitebaseParser* parser) -{ - SQLResultSetField* field = parser->auxField = initSQLResultSetField(parser->heap); - - if ((token = yylex(parser)) == TK_DOT) // table.fieldName - { - CharP alias; - - field->tableName = (CharP)parser->yylval; - if (yylex(parser) != TK_IDENT) - return lbError(ERR_SYNTAX_ERROR, parser); - alias = field->alias = (CharP)TC_heapAlloc(parser->heap, xstrlen(field->tableName) + xstrlen(field->tableColName = (CharP)parser->yylval) + 2); - xstrcpy(alias, field->tableName); - xstrcat(alias, "."); - xstrcat(alias, field->tableColName); - token = yylex(parser); - } - else // A simple field. - field->tableColName = field->alias = (CharP)parser->yylval; - - return token; -} - -/** - * Deals with a data function. - * - * @param token A token witch is possibly a data function token. - * @param parser The parser structure. - * @return The next token or -1 if it is not a data function. - */ -int32 dataFunction(int32 token, LitebaseParser* parser) -{ - int32 function; - - switch (token) - { - case TK_ABS: // Abs function. - function = FUNCTION_DT_ABS; - break; - case TK_DAY: // Day function. - function = FUNCTION_DT_DAY; - break; - case TK_HOUR: // Hour function. - function = FUNCTION_DT_HOUR; - break; - case TK_LOWER: // Lower function. - function = FUNCTION_DT_LOWER; - break; - case TK_MILLIS: // Millis function. - function = FUNCTION_DT_MILLIS; - break; - case TK_MINUTE: // Minute function. - function = FUNCTION_DT_MINUTE; - break; - case TK_MONTH: // Month function. - function = FUNCTION_DT_MONTH; - break; - case TK_SECOND: // Second function. - function = FUNCTION_DT_SECOND; - break; - case TK_UPPER: // Upper function. - function = FUNCTION_DT_UPPER; - break; - case TK_YEAR: // Year function. - function = FUNCTION_DT_YEAR; - break; - default: - return -1; - } - if (yylex(parser) != TK_OPEN || pureField(yylex(parser), parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->auxField->sqlFunction = function; - return yylex(parser); -} - -/** - * Deals with a aggregation function. - * - * @param token A token witch is possibly a data function token. - * @param parser The parser structure. - * @return The next token or -1 if it is not a data function. - */ -int32 aggFunction(int32 token, LitebaseParser* parser) -{ - int function; - - switch (token) - { - case TK_AVG: - function = FUNCTION_AGG_AVG; - break; - case TK_COUNT: - function = FUNCTION_AGG_COUNT; - break; - case TK_MAX: - function = FUNCTION_AGG_MAX; - break; - case TK_MIN: - function = FUNCTION_AGG_MIN; - break; - case TK_SUM: - function = FUNCTION_AGG_SUM; - break; - default: - return -1; - } - if (token == TK_COUNT) - { - if (yylex(parser) != TK_OPEN || yylex(parser) != TK_ASTERISK || yylex(parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->auxField = initSQLResultSetField(parser->heap); - } - else if (yylex(parser) != TK_OPEN || pureField(yylex(parser), parser) != TK_CLOSE) - return lbError(ERR_SYNTAX_ERROR, parser); - parser->auxField->sqlFunction = function; - return yylex(parser); -} - -/** - * Deals with a possible where clause. - * - * @param token The token where if it is a where clause. - * @param parser The parser structure. - * @return The token received if it is not a where clause or the token after the where clause. - */ -int32 optWhereClause(int32 token, LitebaseParser* parser) -{ - if (token == TK_WHERE) // Where clause. - { - SQLBooleanClause* whereClause = getInstanceBooleanClause(parser); - token = expression(yylex(parser), parser); - whereClause->expressionTree = whereClause->origExpressionTree = parser->auxTree; - whereClause->isWhereClause = true; // It indicates that it is a where clause. - } - return token; -} - -/** - * Deals with an order by clause. - * - * @param parser The parser structure. - * @return The first token after the order by clause. - */ -int32 orderByClause(LitebaseParser* parser) -{ - int32 token; - bool direction; - SQLSelectClause* select = &parser->select; - - do - { - direction = true; - - // Ascending or descending order. - if ((token = field(yylex(parser), parser)) == TK_ASC) - token = yylex(parser); - else if (token == TK_DESC) - { - direction = false; - token = yylex(parser); - } - else if (token == PARSER_ERROR) - return PARSER_ERROR; - - select->fieldsCount--; - addColumnFieldOrderGroupBy(direction, true, parser); - } - while (token == TK_COMMA); - - return token; -} - -/** - * Deals with a group by clause. - * - * @param parser The parser structure. - * @return The first token after the group by clause. - */ -int32 groupByClause(LitebaseParser* parser) -{ - int32 token; - SQLSelectClause* select = &parser->select; - - do - { - token = field(yylex(parser), parser); - select->fieldsCount--; - addColumnFieldOrderGroupBy(true, false, parser); - } - while (token == TK_COMMA); - - if (token == TK_HAVING) // Adds the expression tree of the where clause. - { - parser->isWhereClause = false; // Indicates if the clause is a where or a having clause. - token = expression(yylex(parser), parser); - parser->havingClause->expressionTree = parser->auxTree; - } - - return token; -} - - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if the function lbError() in fact creates an exception. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(lbError) -{ - LitebaseParser* parser = (LitebaseParser*)xmalloc(sizeof(LitebaseParser)); - - parser->context = currentContext; - lbError(2, parser); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - xfree(parser); -finish: ; -} - -/** - * Tests if the function lbErrorWithMessage() in fact creates an exception. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(lbErrorWithMessage) -{ - LitebaseParser* parser = (LitebaseParser*)xmalloc(sizeof(LitebaseParser)); - - parser->context = currentContext; - lbErrorWithMessage(getMessage(2), "", parser); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - xfree(parser); -finish: ; -} - -/** - * Tests if the function initLitebaseParser() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -TESTCASE(initLitebaseParser) -{ - Heap heap = heapCreate(); - JChar buffer[400]; - CharP string; - int32 length; - - IF_HEAP_ERROR(heap) - { - heapDestroy(heap); - TEST_FAIL(tc, "OutOfMemoryError"); - goto finish; - } - - // Null string. - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, null, 0, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - // Empty string. - buffer[0] = 0; - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, 0, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - TC_CharP2JCharPBuf(" ", 1, buffer, true); - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, 1, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - TC_CharP2JCharPBuf(" ", 2, buffer, true); - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, 2, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - string = "create table bookentry(name char(30), address char(50), phone char(20), birthday int, salary float, married short, gender short, lastUpdated long)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "CREATE INDEX IDX_0 ON bookentry(rowid)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select rowid, name, address, phone, birthday, salary, married, gender, lastUpdated from bookentry"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "delete bookentry where rowid = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string ="insert into bookentry values (?, ?, ?, ?, ?, ?, ?, ?)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update bookentry set name = ?, address = ?, phone = ?, birthday = ?, salary = ?, married = ?, gender = ?, lastUpdated = ? where rowid = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from bookentry where rowid = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table photodb(name char(20), photo blob(16384))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into photodb values (?,?)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select photo from photodb where name = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table person"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table PERSON (NAME CHAR(8))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person values (?)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person values ('a')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from person where name = 'a'"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "CREATE INDEX IDX_NAME ON PERSON(NAME)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from person"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select count(*) as number from person"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select max(rowid) as number from person"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "CREATE table PERSON (NAME CHAR(10))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person values ('Juliana')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create index idx on person(name)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table person (name char(10))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table person1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table person1 (name char(10) primary key)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table person2"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table person2 (name char(10) primary key)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person1 values ('Name')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person2 values ('Name')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person1 values ('Name100')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person2 values ('Name100')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select rowid, name from person1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select rowid, name from person2"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table person2 (name char(1) primary key)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person2 values('')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person2 values('\\'')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person2 values('\\'A')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from person2"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from person2 where name like '\\''"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table person2 (name char(2) primary key)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into person2 values('\\'AA')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from person2 where name like '\\'%'"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table blob0"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table blob1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table blob2"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table blob3"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob0 (value blob(10 G))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - string = "create table blob0 (value blob(11 M))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - string = "create table blob1 (value blob(100) not null)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob2 (name varchar(10), picture blob(100 K))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob3 (name varchar(10), id int, video blob(1 M))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob0 (value blob(100) primary key)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - string = "create table blob0 (value blob(100), primary key(value))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob0 (value blob(100), age int, primary key(value, age))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob0 (value blob(100), age int, primary key(age, value))"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table blob0 (value blob(100) default null, age int)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(Null, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(NotNull, currentContext->thrownException); - currentContext->thrownException = null; - - string = "create index idx on blob1(value)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create index idx on blob1(value, rowid)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "alter table blob2 add primary key (picture)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "alter table blob2 add primary key (name, picture)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob1 where value > 100"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select value from blob1 order by value"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select value from blob1 group by value"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into blob1 values (1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into blob1 values ('a')"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update blob1 set value = 3 where rowid = 1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob1 where value = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "delete from blob1 where value = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "Insert into blob1 values (?)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into blob2 (picture, name) values (?, ?)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into blob3 (id, video, name) values (?, ?, ?)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create index idx on blob2(name)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob3"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update blob3 set video = ? where rowid = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update blob1 set value = ? where rowid = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update blob2 set picture = ? where rowid = ?"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select value from blob1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select picture from blob2"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select video from blob3"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2 where rowid = 11"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2 where rowid = 12"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "insert into blob2 (picture, name) values (null, 0)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2 where rowid = 13"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob3 where rowid = 10"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update blob3 set video = null where rowid = 10"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2 where rowid = 14"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "update blob2 set picture = ?, name = ? where rowid = 1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2 where name = ''"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob3 order by id desc"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from blob2 where rowid = 1"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 38"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (38L, '38', 'ADENILZA xxxxxxxxxxxxxxxxxxxxxE', 'ADENILZA 111111111111111111111E', '11.111.222//4443-22', '', 1, 1, '', 4, '3423423421', '', '', '1', 20051110101123, 20051110101123, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 114L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (114L, '114', 'NIETO yyyyyyyyyyyyyyyyyyyyE', 'NIETO 2222222222222222222EE', '22.222.333//3333-33', '', 1, 1, '', 4, '4342342423', '', '', '1', 20051110101123, 20051110101123, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 161L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (161L, '161', 'ANTONIO bbbbbbbbbbbbbbbbbbbbbbE', 'ANTONIO 33333333333333333333333', '44.444.444//4441-44', '', 1, 1, '', 4, '5435656458', '', '', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 421L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (421L, '421', 'CLAUDIOMIR cccccccccccccccccMEE', 'CLAUDIOMIR 444444444444444444EE', '55.555.555//5555-26', '', 1, 1, '', 4, '', '', '', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 443L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (443L, '443', 'MARIA dddddddddddddddddSO', 'MARIA 55555555555555555SO', '777.777.777-20', '', 1, 1, '', 4, '6756756756', '', '', '2', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 941L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (941L, '941', 'J.B.eeeeeeeeeeeeeeeeeeeeeeeeeeE', 'J.B.6666666666666666666666666EE', '88.888.888//8889-83', '', 1, 1, '', 4, '8655676565', '', '', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 968L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (968L, '968', 'JARDELIO fffffffffffffffffffffffE', 'JARDELIO 7777777777777777777777EE','99.999.999//9999-82', '', 1, 1, '', 4, '7656456547', '', '', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1217L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (1217L, '1217', 'N.gggggggggggggggE', 'N.C.8888888888888E', '00.000.000//1111-16', '', 1, 1, '', 4, '4532432439', '', '', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1450L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (1450L, '1450', 'OTTO COMhhhhhhhhhhhhhhhhhhhhhhhE', 'OTTO 9999999999999999999999999EE', '22.222.222//2222-17', '', 1, 1, '', 4, '4535345340', '', '', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1585L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "INSERT INTO ACT_CLIENTE VALUES (1585L, '1585', 'OROSINO iiiiiiiiiiiiiiiiiiiE', 'OROSINO 0000000000000000000E', '33.333.333//3333-33', '', 1, 1, '', 4, '5435345357', '', '30 ', '1', 20051110101124, 20051110101124, 1)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "DELETE ACT_CLIENTE WHERE ACTCLIENTEID = 1664L"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "drop table act_cliente"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "create table act_cliente (actclienteid long, actcodcliente char(30), actrazaosocial char(100), actnomefantasia char(100), actie char(20), actcnpj char(20), actstatus long, acttipocliente long, actmail char(255), actvendedorid long, acttelefone char(18), actobservacao char(255), actfax char(18), actcodtabela char(20), actdatinclusao long, actdatalteracao long, actusuarioid long)"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, false, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select actrazaosocial from act_cliente where actrazaosocial='ADENILZA xxxxxxxxxxxxxxxxxxxxxE'"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - string = "select * from act_cliente"; - TC_CharP2JCharPBuf(string, length = xstrlen(string), buffer, true); - ASSERT1_EQUALS(NotNull, initLitebaseParser(currentContext, buffer, length, true, heap)); - ASSERT1_EQUALS(Null, currentContext->thrownException); - - xfree(currentContext->litebasePtr); - heapDestroy(heap); - -finish: ; -} - -#endif diff --git a/LitebaseSDK/src/native/parser/LitebaseParser.h b/LitebaseSDK/src/native/parser/LitebaseParser.h deleted file mode 100644 index daa62308bc..0000000000 --- a/LitebaseSDK/src/native/parser/LitebaseParser.h +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions to initialize, set, and process the parser structure. - */ - -#ifndef LITEBASEPARSER_H -#define LITEBASEPARSER_H - -#include "Litebase.h" - -/** - * Shows a parser error message without an extra message. - * - * @param error An error code. - * @param parser A pointer to the parser structure. - * @return PARSER_ERROR to indicate that an error has occurred. - * @throws SQLParseException To throw an exception if the error message that occurred. - */ -int32 lbError(int32 error, LitebaseParser* parser); - -/** - * Shows a parser error message with an extra message. - * - * @param error An error message. - * @param message An extra error message. - * @param parser A pointer to the parser structure. - * @return PARSER_ERROR to indicate that an error has occurred. - * @throws SQLParseException To throw an exception if the error message that occurred. - */ -int32 lbErrorWithMessage(CharP error, CharP message, LitebaseParser* parser); - -/** - * Initializes and parses a sql string. - * - * @param context The thread context where the function is being executed. - * @param sqlStr The sql unicode string. - * @param sqlLen The length of the sql string. - * @param isSelect Indicates if a sql command is a select. - * @param heap A heap to allocate the parser structure. - * @return A pointer to a LitebaseParser structure if the string does not have parser errors or null, otherwise. - */ -LitebaseParser* initLitebaseParser(Context context, JCharP sqlStr, int32 sqlLen, bool isSelect, Heap heap); - -/** - * Sets the operand type. - * - * @param operandType The type of the operand. - * @param parser A pointer to the parser structure. - * @return A pointer to a SQLBooleanTree structure for a expression tree. - */ -SQLBooleanClauseTree* setOperandType(int32 operandType, LitebaseParser* parse); - -/** - * Adds a column field to the order or group by field list. - * - * @param isAscending Indicates if the order by or group by sorting is in ascending or descending order. - * @param isOrderBy Indicates if the field list where to add a list is a order or group by. - * @param parser A pointer to the parser structure, which contains the list(s). - * @return true if the number of fields has not reached its limit; false, otherwise. - */ -bool addColumnFieldOrderGroupBy(bool isAscending, bool isOrderBy, LitebaseParser* parser); - -/** - * Gets a especific boolean clause: it will get a where clause if there is a where clause; otherwise, it will get a having clause. - * - * @param parser A pointer to the parser structure. - * @return A pointer to a SQLBooleanClause. - */ -SQLBooleanClause* getInstanceBooleanClause(LitebaseParser* parser); - -/** - * Initializes a field definition for a table being created. - * - * @param fieldName The name of the new field. - * @param fieldType The type of the new field. - * @param fieldSize The size of the new field (it is zero if the type is not CHARS or CHARS_NOCASE). - * @param isPrimaryKey Indicates if the new field is the primary key. - * @param defaultValue The default value of the new field (it can be null if there is no default value). - * @param isNotNull Indicates if the new field can be null or not. - * @return a pointer to the new result set table structure. - */ -SQLFieldDefinition* initSQLFieldDefinition(CharP fieldName, int32 fieldType, int32 fieldSize, bool isPrimaryKey, JCharP defaultValue, bool isNotNull, - Heap heap); - -/** - * Initializes a result set field. - * - * @param heap A heap to alocate the result set field structure. - * @return A pointer to a result set field structure. - */ -SQLResultSetField* initSQLResultSetField(Heap heap); - -/** - * Initializes a result set table structure using a table name and its optional alias name. - * - * @param tableName the name of the new table. - * @param aliasTableName the optional alias name of the new table (it can be null). - * @return a pointer to the new result set table structure. - */ -SQLResultSetTable* initSQLResultSetTable(CharP tableName, CharP aliasTableName, Heap heap); - -/** - * The function that parses the sql string. - * - * @param parser The parser structure. - * @return true if there are parser errors; false, otherwise. - */ -bool yyparse(LitebaseParser* parser); - -/** - * Deals with a list of identifiers separated by commas. - * - * @param parser The parser structure. - * @return The token after the list of identifiers. - */ -int32 colnameCommaList(LitebaseParser* parser); - -/** - * Deals with a list of rows of a table being created. - * - * @param parser The parser structure. - * @return The token after the list of rows. - */ -int32 createColumnCommalist(LitebaseParser* parser); - -/** - * Deals with a column declaration. - * - * @param parser The parser structure. - * @return The token after a column declaration. - */ -int32 createColumn(LitebaseParser* parser); - -/** - * Deals with an expression of an expression tree of a where clause. - * - * @param token The first token of the expression. - * @param parser The parser structure. - * @return The token after the expression. - */ -int32 expression(int32 token, LitebaseParser* parser); - -/** - * Deals with a term of an expression tree of a where clause. - * - * @param token The first token of the term. - * @param parser The parser structure. - * @return The token after the term. - */ -int32 term(int32 token, LitebaseParser* parser); - -/** - * Deals with a factor of an expression tree of a where clause. - * - * @param token The first token of the factor. - * @param parser The parser structure. - * @return The token after the factor. - */ -int32 factor(int32 token, LitebaseParser* parser); - -/** - * Deals with a single expression of an expression tree of a where clause. - * - * @param token The first token of the single expression. - * @param parser The parser structure. - * @return The token after the single expression. - */ -int32 singleExp(int32 token, LitebaseParser* parser); - -/** - * Deals with a list of values of an insert. - * - * @param parser The parser structure. - * @return The token after the list of values. - */ -int32 listValues(LitebaseParser* parser); - -/** - * Deals with a table list of a select. - * - * @param parser The parser structure. - * @return The token after the list of tables. - */ -int32 tableList(LitebaseParser* parser); - -/** - * Deals with a list of expressions of a select. - * - * @param parser The parser structure. - * @return The token after the list of expressions. - */ -int32 fieldExp(int32 token, LitebaseParser* parser); - -/** - * Deals with a list of update expressions. - * - * @param parser The parser structure. - * @return The token after the list of update expressions. - */ -int32 updateExpCommalist(LitebaseParser* parser); - -/** - * Deals with a field. - * - * @param token A token to be used by the field. - * @param parser The parser structure. - * @return The token after the field. - */ -int32 field(int32 token, LitebaseParser* parser); - -/** - * Deals with a pure field. - * - * @param token A token to be used by the pure field. - * @param parser The parser structure. - * @return The token after the pure field. - */ -int32 pureField(int32 token, LitebaseParser* parser); - -/** - * Deals with a data function. - * - * @param token A token witch is possibly a data function token. - * @param parser The parser structure. - * @return The next token or -1 if it is not a data function. - */ -int32 dataFunction(int32 token, LitebaseParser* parser); - -/** - * Deals with a aggregation function. - * - * @param token A token witch is possibly a data function token. - * @param parser The parser structure. - * @return The next token or -1 if it is not a data function. - */ -int32 aggFunction(int32 token, LitebaseParser* parser); - -/** - * Deals with a possible where clause. - * - * @param token The token where if it is a where clause. - * @param parser The parser structure. - * @return The token received if it is not a where clause or the token after the where clause. - */ -int32 optWhereClause(int32 token, LitebaseParser* parser); - -/** - * Deals with an order by clause. - * - * @param parser The parser structure. - * @return The first token after the order by clause. - */ -int32 orderByClause(LitebaseParser* parser); - -/** - * Deals with a group by clause. - * - * @param parser The parser structure. - * @return The first token after the group by clause. - */ -int32 groupByClause(LitebaseParser* parser); - -#ifdef ENABLE_TEST_SUITE - -/** - * Tests if the function lbError() in fact creates an exception. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_lbError(TestSuite* testSuite, Context currentContext); - -/** - * Tests if the function lbErrorWithMessage() in fact creates an exception. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_lbErrorWithMessage(TestSuite* testSuite, Context currentContext); - -/** - * Tests if the function initLitebaseParser() works properly. - * - * @param testSuite The test structure. - * @param currentContext The thread context where the test is being executed. - */ -void test_initLitebaseParser(TestSuite* testSuite, Context currentContext); - -#endif - -#endif diff --git a/LitebaseSDK/src/native/parser/SQLBooleanClause.c b/LitebaseSDK/src/native/parser/SQLBooleanClause.c deleted file mode 100644 index 8c2f4b9106..0000000000 --- a/LitebaseSDK/src/native/parser/SQLBooleanClause.c +++ /dev/null @@ -1,819 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Internal use only. Represents a boolean clause (WHERE or HAVING) in a SQL query. - */ - -#include "SQLBooleanClause.h" - -/** - * Creates and initializes a boolean clause. - * - * @param heap The heap to allocate a SQLBooleanClause structure. - * @return A pointer to a SQLBooleanClause structure. - */ -SQLBooleanClause* initSQLBooleanClause(Heap heap) -{ - TRACE("sqlBooleanClause") - SQLBooleanClause* sqlBooleanClause = (SQLBooleanClause*)TC_heapAlloc(heap, sizeof(SQLBooleanClause)); - sqlBooleanClause->fieldName2Index = TC_htNew(MAXIMUMS, heap); - sqlBooleanClause->appliedIndexRs = sqlBooleanClause->type = -1; - return sqlBooleanClause; -} - -// juliana@226_3: improved index application. -/** - * Applies the table indices to the boolean clause. The method will possibly transform the SQL boolean tree, eliminating the branches that can be - * resolved through the table indexes. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param tableIndices The table indices; each position in the array relates to a column in the table; a null value indicates no - * index on that column. - * @param columnsCount The number of columns of the table. - * @param hasComposedIndex Indicates if the table has a composed index. - * @return true, if any table index was applied to the boolean clause; false, otherwise. - */ -bool applyTableIndexes(SQLBooleanClause* booleanClause, Index** tableIndexes, int32 columnsCount, bool hasComposedIndex) -{ - TRACE("applyTableIndexes") - SQLResultSetField** fieldList = booleanClause->fieldList; - - // juliana@223_2: corrected a bug that would throw an exception if the where clause if of the form 1 = 1. - Table* table = *fieldList? (*fieldList)->table : null; - - int32 i = columnsCount, - j, - curOperandType, - leftOperandType, - count, - fieldsCount = booleanClause->fieldsCount, - rightOperandType, - numberComposedIndexes = table? table->numberComposedIndexes : 0, - countAppliedIndices = 0; - bool appliedComposedIndex, - isLeft = false; - uint8 columns[MAXIMUMS + 1]; - uint8 operators[MAXIMUMS + 1]; - SQLBooleanClauseTree* curTree; - SQLBooleanClauseTree* leftTree; - SQLBooleanClauseTree* rightTree; - SQLBooleanClauseTree* originalTree; - SQLBooleanClauseTree* indexesValueTree[MAXIMUMS + 1]; - ComposedIndex** composedIndexes = table? table->composedIndexes : null; - ComposedIndex** appliedComposedIndexes = booleanClause->appliedComposedIndexes; - ComposedIndex* currCompIndex; - - if (!booleanClause->isWhereClause) // Indices can only be applied to the where clause. - return false; - - if (!hasComposedIndex) // Verifies if it has simple indices. - { - while (--i >= 0 && !tableIndexes[i]); - - if (i < 0) // If there are no indices, returns. - return false; - } - - // Traverses the tree, from the parent to the rightmost tree until the boolean operator changes. To simplify the algorithm and considering - // that complex boolean expressions (the ones enclosed by parenthesis) are always connected to the left branch), only the branches connected - // to the right side of the tree are candidates to be replaced by table indexing. - curTree = booleanClause->expressionTree; - booleanClause->appliedIndexesBooleanOp = OP_NONE; - - while (curTree) - { - leftTree = curTree->leftTree; - rightTree = curTree->rightTree; - - switch (curOperandType = curTree->operandType) // Checks the type of operand. - { - // juliana@214_4: nots were removed. - - case OP_BOOLEAN_AND: - case OP_BOOLEAN_OR: - { - - // Checks if the boolean connector is different than the previous one. If so, leaves the loop, since for now, for simplicty, the - // algorithm does not combine different boolean operators. - if (booleanClause->appliedIndexesBooleanOp && booleanClause->appliedIndexesBooleanOp != curOperandType) - { - curTree = null; - break; - } - - booleanClause->appliedIndexesBooleanOp = curOperandType; - - // Checks if the left tree has a simple boolean operand. If so, try to apply an index on it. - leftOperandType = leftTree->operandType; - appliedComposedIndex = false; - if ((leftOperandType >= OP_REL_EQUAL && leftOperandType <= OP_REL_LESS_EQUAL) - || ((leftOperandType == OP_PAT_MATCH_LIKE || leftOperandType == OP_PAT_MATCH_NOT_LIKE) - && leftTree->patternMatchType == PAT_MATCH_STARTS_WITH)) - { - // juliana@250_2: corrected a problem of composed indices not returning the expected result. - if (hasComposedIndex && curOperandType == OP_BOOLEAN_AND && !isLeft) // First verifies if it can apply a composed index. - { - originalTree = curTree; - count = 0; - xmemzero(indexesValueTree, fieldsCount * TSIZE); - xmemzero(columns, fieldsCount); - xmemzero(operators, fieldsCount); - - while (true) - { - - getBranchProperties(leftTree, columns, operators, indexesValueTree, count, fieldsCount); - - // Limitation; Composed index only for EQUALS. A composed index can't be applied if the column is not part of the index. - if (count >= fieldsCount || operators[count] != OP_REL_EQUAL || operators[count] == 255) - { - count = 0; - break; // Doesn't apply the composed index. - } - - count++; - - // Verifies if the right operator is one of the above. - if ((rightOperandType = rightTree->operandType) == OP_BOOLEAN_AND) - { - curTree = rightTree; - rightTree = curTree->rightTree; - } - else if ((leftOperandType >= OP_REL_EQUAL && leftOperandType <= OP_REL_LESS_EQUAL) - || ((leftOperandType == OP_PAT_MATCH_LIKE || leftOperandType == OP_PAT_MATCH_NOT_LIKE) - && leftTree->patternMatchType == PAT_MATCH_STARTS_WITH)) - { - getBranchProperties(rightTree, columns, operators, indexesValueTree, count, fieldsCount); - - // Limitation: composed index only for EQUALS. - if (count >= fieldsCount || operators[count] != OP_REL_EQUAL || operators[count] == 255) - { - count = 0; - break; // Doesn't apply the composed index. - } - count++; - break; - } - else - break; // The next operator is an OR, ends the loop. - } - if (count >= 2) // It has an AND operator on at least 2 fields? - { - i = numberComposedIndexes; - while (--i >= 0) - { - currCompIndex = composedIndexes[i]; - appliedComposedIndex = false; - - // juliana@202_6: A composed index can only be used if all its columns are "ANDED" in the where clause. - if (fieldsCount >= currCompIndex->numberColumns) - { - appliedComposedIndex = true; - j = currCompIndex->numberColumns; - while (--j >= 0) - if (columns[j] != currCompIndex->columns[j]) - { - appliedComposedIndex = false; - break; - } - } - - if (appliedComposedIndex) - { - appliedComposedIndexes[booleanClause->appliedIndexesCount] = currCompIndex; - curTree = applyComposedIndexToBranch(booleanClause, originalTree, columns, operators, indexesValueTree, currCompIndex); - break; - } - else - curTree = originalTree; - } - } - } - if (!appliedComposedIndex) - applyIndexToBranch(booleanClause, leftTree, tableIndexes, isLeft); - } - - if (!appliedComposedIndex) // Goes to the right tree. - { - if (isLeft) - curTree = leftTree; - else - curTree = rightTree; - } - break; - } - - // Reached the rightmost node. Triwal to apply the index and ends the loop. - case OP_PAT_MATCH_NOT_LIKE: - case OP_PAT_MATCH_LIKE: - if (rightTree->patternMatchType != PAT_MATCH_STARTS_WITH - && rightTree->patternMatchType != PAT_MATCH_EQUAL) - { - curTree = null; - break; - } - // else falls through. - case OP_REL_EQUAL: - case OP_REL_DIFF: - case OP_REL_GREATER: - case OP_REL_GREATER_EQUAL: - case OP_REL_LESS: - case OP_REL_LESS_EQUAL: - countAppliedIndices = booleanClause->appliedIndexesCount; - applyIndexToBranch(booleanClause, curTree, tableIndexes, isLeft); - if (countAppliedIndices == booleanClause->appliedIndexesCount) - curTree = null; - else - curTree = booleanClause->expressionTree; - break; - - default: // Anything else, stops the loop. - curTree = null; - } - - if (booleanClause->appliedIndexesCount == MAX_NUM_INDEXES_APPLIED) // If the number of indexes to be applied reached the limit leaves the loop. - break; - - if (!curTree && !booleanClause->appliedIndexesCount && !isLeft) - { - isLeft = true; - curTree = booleanClause->expressionTree; - } - } - return booleanClause->appliedIndexesCount > 0; -} - -/** - * Tries to apply an index to a branch of the expression tree that contains a relational expression. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param branch A branch of the expression tree. - * @param indexMap An index bitmap. - * @param isLeft Indicates if the index is being applied to the left branch. - */ -void applyIndexToBranch(SQLBooleanClause* booleanClause, SQLBooleanClauseTree* branch, Index** indexesMap, bool isLeft) -{ - TRACE("applyIndexToBranch") - int32 relationalOp = branch->operandType; - - // Checks if the relational expression involves a column and a constant. - SQLBooleanClauseTree* left = branch->leftTree; - SQLBooleanClauseTree* right = branch->rightTree; - - bool leftIsColumn = (left->operandType == OP_IDENTIFIER); - bool rightIsColumn = (right->operandType == OP_IDENTIFIER); - - if (leftIsColumn != rightIsColumn) - { - int32 column = (leftIsColumn? left->colIndex : right->colIndex), - i = booleanClause->fieldsCount; - uint8* appliedIndexesCols = booleanClause->appliedIndexesCols; - uint8* appliedIndexesRelOps = booleanClause->appliedIndexesRelOps; - SQLBooleanClauseTree** appliedIndexesValueTree = booleanClause->appliedIndexesValueTree; - SQLResultSetField** fieldList = booleanClause->fieldList; - - while (--i >= 0) // An index cannot be applied to a function in the where clause. - if (fieldList[i]->tableColIndex == column && fieldList[i]->isDataTypeFunction) - return; - - if (indexesMap[column]) // Checks if the column is indexed. - { - SQLBooleanClauseTree* parent = branch->parent; - - // Adds the index to the list of applied indexes. - int32 n = booleanClause->appliedIndexesCount++; - appliedIndexesCols[n] = column; - appliedIndexesValueTree[n] = leftIsColumn? right : left; - appliedIndexesRelOps[n] = relationalOp; - - // Remove the branch from the expression tree. - if (parent) - { - SQLBooleanClauseTree* sibling; - SQLBooleanClauseTree* grandParent; - sibling = (branch == parent->leftTree)? parent->rightTree : parent->leftTree; - grandParent = parent->parent; - - // Links the branch sibling to its grandparent, removing the branch from the tree, as result. - if (grandParent) - { - if (isLeft) - grandParent->leftTree = sibling; - else - grandParent->rightTree = sibling; - } - else - booleanClause->expressionTree = sibling; - - sibling->parent = grandParent; - } - else // Removes the branch from the expression tree. - booleanClause->expressionTree = null; // The branch has no parent. So, no expression tree will be left. - } - } -} - -/** - * Applies the composed index and removes the correspondent branch of the tree. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param branch A branch of the expression tree. - * @param columns The columns present in the expression tree. - * @param operators The operators of the expression tree. - * @param indexesValueTree The part of the tree that uses indices. - * @param compIndex The composed index. - * @return The current branch of the tree. - */ -SQLBooleanClauseTree* applyComposedIndexToBranch(SQLBooleanClause* booleanClause, SQLBooleanClauseTree* branch, uint8* columns, uint8* operators, - SQLBooleanClauseTree** indexesValueTree, ComposedIndex* compIndex) -{ - TRACE("applyComposedIndexToBranch") - int32 i = -1, - length = compIndex->numberColumns, - n; - uint8* appliedIndexesCols = booleanClause->appliedIndexesCols; - uint8* appliedIndexesRelOps = booleanClause->appliedIndexesRelOps; - SQLBooleanClauseTree** appliedIndexesValueTree = booleanClause->appliedIndexesValueTree; - SQLBooleanClauseTree* parent = branch->parent; - SQLBooleanClauseTree* root = branch; - - while (++i < length) // Checks if the column is indexed. - { - // Adds the index to the list of applied indexes. - appliedIndexesCols[n = booleanClause->appliedIndexesCount++] = columns[i]; - appliedIndexesValueTree[n] = indexesValueTree[i]; - appliedIndexesRelOps[n] = operators[i]; - } - - while (--i >= 0) - branch = branch->rightTree; - - if (parent) - { - branch->parent = parent; - root->parent->rightTree = branch; - return branch; - } - else - { - branch->parent = null; - if (branch->operandType != OP_BOOLEAN_AND && branch->operandType != OP_BOOLEAN_OR) // Is the end of the root? - branch = null; - booleanClause->expressionTree = branch; - return branch; - } -} - -// juliana@253_7: improved index application on filters when using joins. -// juliana@226_3: improved index application. -/** - * Applies the table indexes to the boolean clause. The method will possibly transform the SQL boolean tree, to eliminate the branches that can be - * resolved through the table indexes. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @return true, if any table index was applied to the boolean clause; false, otherwise. - */ -bool applyTableIndexesJoin(SQLBooleanClause* booleanClause) -{ - TRACE("applyTableIndexesJoin") - SQLBooleanClauseTree* curTree; - SQLBooleanClauseTree* leftTree; - SQLBooleanClauseTree* rightTree; - int32 curOperandType, - leftOperandType, - countAppliedIndices = 0; - bool isLeft = false; - - if (!booleanClause->isWhereClause) // Indexes can only be applied to a where clause. - return false; - - // Traverses the tree, from the parent to the rightmost tree, until the boolean operator changes. To simplify the algorithm and considering - // that complex boolean expressions (the ones enclosed by parenthesis) are always connected to the left branch), only the branches connected to - // the right side of the tree are candidates to be replaced by table indexing. - curTree = booleanClause->expressionTree; - booleanClause->appliedIndexesBooleanOp = OP_NONE; - - while (curTree) - { - leftTree = curTree->leftTree; - rightTree = curTree->rightTree; - - switch (curOperandType = curTree->operandType) // Checks the type of operand. - { - // juliana@214_4: nots were removed. - - case OP_BOOLEAN_AND: - case OP_BOOLEAN_OR: - { - - // Checks if the boolean connector is different than the previous one. If so, leaves the loop, since for now, for simplicty, the - // algorithm does not combine different boolean operators. - if (booleanClause->appliedIndexesBooleanOp != OP_NONE && booleanClause->appliedIndexesBooleanOp != curOperandType) - { - curTree = null; - break; - } - - booleanClause->appliedIndexesBooleanOp = curOperandType; - - if (booleanClause->appliedIndexRs == -1) - booleanClause->appliedIndexRs = leftTree->indexRs; - - // Checks if the left tree has a simple boolean operand. If so, tries to apply an index on it. - leftOperandType = leftTree->operandType; - - if ((leftOperandType >= OP_REL_EQUAL && leftOperandType <= OP_REL_LESS_EQUAL) - || ((leftOperandType == OP_PAT_MATCH_LIKE || leftOperandType == OP_PAT_MATCH_NOT_LIKE) - && leftTree->patternMatchType == PAT_MATCH_STARTS_WITH)) - applyIndexToBranchJoin(booleanClause, leftTree, isLeft); - - if (curTree->rightTree->indexRs != booleanClause->appliedIndexRs) - { - if (curOperandType == OP_BOOLEAN_AND) - booleanClause->type = WC_TYPE_AND_DIFF_RS; - else // 'OR' of different resultsets, leaves the loop. - { - booleanClause->type = WC_TYPE_OR_DIFF_RS; - curTree = null; - break; - } - } - if (isLeft) - curTree = leftTree; - else - curTree = rightTree; - break; - } - - // Reached the rightmost node. Tries to apply the index and ends the loop. - case OP_PAT_MATCH_NOT_LIKE: - case OP_PAT_MATCH_LIKE: - if (rightTree->patternMatchType != PAT_MATCH_STARTS_WITH && rightTree->patternMatchType != PAT_MATCH_EQUAL) - { - curTree = null; - break; - } - - // Else falls through. - case OP_REL_EQUAL: - case OP_REL_DIFF: - case OP_REL_GREATER: - case OP_REL_GREATER_EQUAL: - case OP_REL_LESS: - case OP_REL_LESS_EQUAL: - countAppliedIndices = booleanClause->appliedIndexesCount; - applyIndexToBranchJoin(booleanClause, curTree, isLeft); - if (countAppliedIndices == booleanClause->appliedIndexesCount) - curTree = null; - else - curTree = booleanClause->expressionTree; - break; - - default: // Anything else, stops the loop. - curTree = null; - } - - // If the number of indexes to be applied reached the limit, leaves the loop. - if (booleanClause->appliedIndexesCount == MAX_NUM_INDEXES_APPLIED) - break; - - if (!curTree && !booleanClause->appliedIndexesCount && !isLeft) - { - isLeft = true; - curTree = booleanClause->expressionTree; - } - } - return booleanClause->appliedIndexesCount > 0; -} - -// juliana@253_7: improved index application on filters when using joins. -/** - * Tries to apply an index to a branch of the expression tree that contains a relational expression. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param branch The branch of the expression tree. - * @param isLeft Indicates if the index is being applied to the left branch. - */ -void applyIndexToBranchJoin(SQLBooleanClause* booleanClause, SQLBooleanClauseTree* branch, bool isLeft) -{ - TRACE("applyIndexToBranchJoin") - int32 relationalOp = branch->operandType; - - // Checks if the relational expression involves a column and a constant. - SQLBooleanClauseTree* tree; - SQLBooleanClauseTree* left = branch->leftTree; - SQLBooleanClauseTree* right = branch->rightTree; - - bool leftIsColumn = left->operandType == OP_IDENTIFIER; - bool rightIsColumn = right->operandType == OP_IDENTIFIER; - SQLResultSetField** fieldList = booleanClause->fieldList; - Table* table; - SQLResultSetField* field; - int32 fieldIndex; - - if (branch->bothAreIdentifier && fieldList[fieldIndex = getFieldIndex(right)]->table->columnIndexes[right->colIndex]) - right->hasIndex = true; - - if (leftIsColumn != rightIsColumn) - { - Table** appliedIndexTables; - int32 column; - uint8* appliedIndexesCols = booleanClause->appliedIndexesCols; - SQLBooleanClauseTree** appliedIndexesValueTree = booleanClause->appliedIndexesValueTree; - uint8* appliedIndexesRelOps = booleanClause->appliedIndexesRelOps; - appliedIndexTables = booleanClause->appliedIndexesTables; - - if (leftIsColumn) - column = (tree = left)->colIndex; - else - column = (tree = right)->colIndex; - - // juliana@285_1: solved a possible wrong result if the query had join and a filter with function in a column with an index. - // Checks if the column is indexed. - if ((table = (field = fieldList[fieldIndex = getFieldIndex(tree)])->table)->columnIndexes[column] && !field->isDataTypeFunction) - { - // Adds the index to the list of applied indexes. - int32 n = booleanClause->appliedIndexesCount++; - SQLBooleanClauseTree* parent = branch->parent; - appliedIndexesCols[n] = column; - appliedIndexesValueTree[n] = leftIsColumn? right: left; - appliedIndexesRelOps[n] = relationalOp; - appliedIndexTables[n] = table; - - // Remove the branch from the expression tree. - if (parent) - { - SQLBooleanClauseTree* sibling = (branch == parent->leftTree)? parent->rightTree : parent->leftTree; - SQLBooleanClauseTree* grandParent = parent->parent; - - // Links the branch sibling to its grandparent, removing the branch from the tree, as result. - if (grandParent) - { - if (isLeft) - grandParent->leftTree = sibling; - else - grandParent->rightTree = sibling; - } - else - booleanClause->expressionTree = sibling; - sibling->parent = grandParent; - } - else - booleanClause->expressionTree = null; // The branch has no parent. So, no expression tree will be left. - } - } -} - -/** - * Evaluate the boolean clause, accordingly to values of the current record of the given ResultSet. - * - * @param context The thread context where the function is being executed. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @return true if all parameter are defined; false, otherwise. - * @throws DriverException if a parameter is not defined. - */ -bool sqlBooleanClausePreVerify(Context context, SQLBooleanClause* booleanClause) -{ - TRACE("sqlBooleanClausePreVerify") - if (booleanClause) // guich@504_14 - { - // Checks if there are parameters defined in the clause and if all them had their values assigned. - SQLBooleanClauseTree** paramList = booleanClause->paramList; - int32 i = booleanClause->paramCount; - while(--i >= 0) - if (!paramList[i]->isParamValueDefined) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_PARAMETER_NOT_DEFINED), i); - return false; - } - } - return true; -} - -/** - * Evaluates the boolean clause, accordingly to values of the current record of the given ResultSet. - * - * @param resultSet the ResultSet used for the evaluation. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return 1, if the current record of the result set satisfies the boolean clause; 0 if the current record of the result set does not satisfy the - * boolean clause; -1, otherwise. - */ -int32 sqlBooleanClauseSatisfied(Context context, SQLBooleanClause* booleanClause, ResultSet* resultSet, Heap heap) -{ - TRACE("sqlBooleanClauseSatisfied") - booleanClause->resultSet = resultSet; - return booleanTreeEvaluate(context, booleanClause->expressionTree, heap); -} - -/** - * Binds the column information of the underlying table list to the boolean clause. - * - * @param context The thread context where the function is being executed. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param names2Index IntHashtable that maps the column names to the column indexes. - * @param columnTypes The data types of each column. - * @param tableList The table list of the select clause. - * @param tableListSize The number of tables of the table list. - * @param heap A heap to allocate some new SQLBooleanClauseTree nodes. - * @return true, if the boolean clause was bound successfully; false, otherwise. - */ -bool bindColumnsSQLBooleanClause(Context context, SQLBooleanClause* booleanClause, Hashtable* names2Index, int8* columnTypes, - SQLResultSetTable** tableList, int32 tableListSize, Heap heap) -{ - TRACE("bindColumnsSQLBooleanClause") - - // These two are only used in the expressionTree. - if (tableList != null) // The having clause has already been verified. - { - if (!verifyColumnNamesOnTableList(context, booleanClause->fieldList, booleanClause->fieldsCount, tableList, tableListSize)) - return false; - } - else // Rearranges the having clause. - { - SQLResultSetField* field; - SQLResultSetField** fieldList = booleanClause->fieldList; - int32 i = booleanClause->fieldsCount; - while (--i >= 0) - { - field = fieldList[i]; - field->tableColIndex = TC_htGet32Inv(names2Index, field->aliasHashCode); // Sets the column indexes of the temp table. - if (field->sqlFunction == FUNCTION_DT_NONE) - field->dataType = (int8)columnTypes[field->tableColIndex]; - } - } - booleanClause->expressionTree = removeNots(booleanClause->expressionTree, heap); // juliana@214_4 - return bindColumnsSQLBooleanClauseTree(context, booleanClause->expressionTree); // Binds the field information in the tree to the table columns. -} - -/** - * Verifies if the column names are correct and belongs to the table list and is used only to verify if where clause and having clause field list - * is the field list of the where/having clause. - * - * @param context The thread context where the function is being executed. - * @param fieldList The field list of the where/having clause. - * @param fieldsCount The number of fields. - * @param tableList The table list. - * @param tableListSize The numbers of tables of the table list. - * @return true, if field name verification found no problems; false, otherwise. - * @throws SQLParseException If there is an unknown or an ambiguos column name. - */ -bool verifyColumnNamesOnTableList(Context context, SQLResultSetField** fieldList, int32 fieldsCount, SQLResultSetTable** tableList, - int32 tableListSize) -{ - TRACE("verifyColumnNamesOnTableList") - int32 index, - hashAliasTableName, - j; - bool foundFirst; - Table* currentTable; - SQLResultSetField* field; - - while (--fieldsCount >= 0) - { - if ((field = fieldList[fieldsCount])->tableName) - { - hashAliasTableName = TC_hashCode(field->tableName); - j = -1; - - // Verifies if it is a valid table name. - currentTable = null; - while (++j < tableListSize) - { - if (tableList[j]->aliasTableNameHashCode == hashAliasTableName) - { - currentTable = tableList[j]->table; - break; - } - } - if (!currentTable || (index = TC_htGet32Inv(¤tTable->htName2index, TC_hashCode(field->tableColName))) < 0) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), field->alias); - return false; - } - field->table = currentTable; - field->tableColIndex = index; - if (field->sqlFunction == FUNCTION_DT_NONE) - field->dataType = (int8)currentTable->columnTypes[index]; - else - field->parameter->dataType = (int8)currentTable->columnTypes[index]; - field->indexRs = j; - } - else // Verifies if the column name in field list is ambiguous. - { - j = -1; - foundFirst = false; - while (++j < tableListSize) - { - index = TC_htGet32Inv(&(currentTable = tableList[j]->table)->htName2index, field->tableColHashCode); - if (index >= 0) - { - if (foundFirst) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_AMBIGUOUS_COLUMN_NAME), field->alias); - return false; - } - else - { - foundFirst = true; - field->table = currentTable; - field->tableColIndex = index; - if (field->sqlFunction == FUNCTION_DT_NONE) - field->dataType = (int8)currentTable->columnTypes[index]; - else - field->parameter->dataType = (int8)currentTable->columnTypes[index]; - field->indexRs = j; - } - } - } - if (!foundFirst) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), fieldList[fieldsCount]->alias); - return false; - } - } - - } - return true; -} - -/** - * Binds the column information of the underlying table to the boolean clause. - * - * @param context The thread context where the function is being executed. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param rsTable The SQLResultSetTable table of the update or delete statement. - * @param heap A heap to allocate some new SQLBooleanClauseTree nodes. - * @return true, if the boolean clause was bound successfully; false, otherwise. - */ -bool bindColumnsSQLBooleanClauseSimple(Context context, SQLBooleanClause* clause, SQLResultSetTable* rsTable, Heap heap) -{ - TRACE("bindColumnsSQLBooleanClauseSimple") - - if (!verifyColumnNamesOnTable(context, clause->fieldList, clause->fieldsCount, rsTable)) - return false; - - clause->expressionTree = removeNots(clause->expressionTree, heap); // juliana@214_4 - return bindColumnsSQLBooleanClauseTree(context, clause->expressionTree); // Binds the field information in the tree to the table columns. -} - -/** - * Verifies if the column names are correct and belongs to the table and is used only to verify if where clause field list is the field list of - * the where clause. - * - * @param context The thread context where the function is being executed. - * @param fieldList The field list of the where/having clause. - * @param fieldsCount The number of fields. - * @param rsTable The SQLResultSetTable table of the update or delete statement. - * @return true, if field name verification found no problems; false, otherwise. - * @throws SQLParseException If there is an unknown or an ambiguos column name. - */ -bool verifyColumnNamesOnTable(Context context, SQLResultSetField** fieldList, int32 fieldsCount, SQLResultSetTable* rsTable) -{ - TRACE("verifyColumnNamesOnTable") - int32 index; - Table* currentTable; - SQLResultSetField* field; - - while (--fieldsCount >= 0) - { - if ((field = fieldList[fieldsCount])->tableName) - { - // Verifies if it is a valid table name. - if (rsTable->aliasTableNameHashCode != TC_hashCode(field->tableName) - || (index = TC_htGet32Inv(&(currentTable = rsTable->table)->htName2index, TC_hashCode(field->tableColName))) < 0) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), field->alias); - return false; - } - field->table = currentTable; - field->tableColIndex = index; - if (field->sqlFunction == FUNCTION_DT_NONE) - field->dataType = (int8)currentTable->columnTypes[index]; - else - field->parameter->dataType = (int8)currentTable->columnTypes[index]; - } - else // Verifies if the column name in field list is ambiguous. - { - if ((index = TC_htGet32Inv(&(currentTable = rsTable->table)->htName2index, field->tableColHashCode)) >= 0) - { - field->table = currentTable; - field->tableColIndex = index; - if (field->sqlFunction == FUNCTION_DT_NONE) - field->dataType = (int8)currentTable->columnTypes[index]; - else - field->parameter->dataType = (int8)currentTable->columnTypes[index]; - } - else - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), field->alias); - return false; - } - } - - } - return true; -} diff --git a/LitebaseSDK/src/native/parser/SQLBooleanClause.h b/LitebaseSDK/src/native/parser/SQLBooleanClause.h deleted file mode 100644 index b89078bb48..0000000000 --- a/LitebaseSDK/src/native/parser/SQLBooleanClause.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Internal use only. Represents a boolean clause (WHERE or HAVING) in a SQL query. - */ - -#ifndef SQLBOOLEANCLAUSE_H -#define SQLBOOLEANCLAUSE_H - -#include "Litebase.h" - -/** - * Creates and initializes a boolean clause. - * - * @param heap The heap to allocate a SQLBooleanClause structure. - * @return A pointer to a SQLBooleanClause structure. - */ -SQLBooleanClause* initSQLBooleanClause(Heap heap); - -/** - * Applies the table indices to the boolean clause. The method will possibly transform the SQL boolean tree, eliminating the branches that can be - * resolved through the table indexes. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param tableIndices The table indices; each position in the array relates to a column in the table; a null value indicates no - * index on that column. - * @param columnsCount The number of columns of the table. - * @param hasComposedIndex Indicates if the table has a composed index. - * @return true, if any table index was applied to the boolean clause; false, otherwise. - */ -bool applyTableIndexes(SQLBooleanClause* booleanClause, Index** tableIndexes, int32 columnsCount, bool hasComposedIndex); - -/** - * Tries to apply an index to a branch of the expression tree that contains a relational expression. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param branch A branch of the expression tree. - * @param isLeft Indicates if the index is being applied to the left branch. - * @param indexMap An index bitmap. - */ -void applyIndexToBranch(SQLBooleanClause* booleanClause, SQLBooleanClauseTree* branch, Index** indexesMap, bool isLeft); - -/** - * Applies the composed index and removes the correspondent branch of the tree. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param branch A branch of the expression tree. - * @param columns The columns present in the expression tree. - * @param operators The operators of the expression tree. - * @param indexesValueTree The part of the tree that uses indices. - * @param compIndex The composed index. - * @return The current branch of the tree. - */ -SQLBooleanClauseTree* applyComposedIndexToBranch(SQLBooleanClause* booleanClause, SQLBooleanClauseTree* branch, uint8* columns, uint8* operators, - SQLBooleanClauseTree** indexesValueTree, ComposedIndex* compIndex); - -/** - * Applies the table indexes to the boolean clause. The method will possibly transform the SQL boolean tree, to eliminate the branches that can be - * resolved through the table indexes. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @return true, if any table index was applied to the boolean clause; false, otherwise. - */ -bool applyTableIndexesJoin(SQLBooleanClause* booleanClause); - -// juliana@noidr_3: improved index application on filters when using joins. -/** - * Tries to apply an index to a branch of the expression tree that contains a relational expression. - * - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param branch The branch of the expression tree. - * @param isLeft Indicates if the index is being applied to the left branch. - */ -void applyIndexToBranchJoin(SQLBooleanClause* booleanClause, SQLBooleanClauseTree* branch, bool isLeft); - -/** - * Evaluate the boolean clause, accordingly to values of the current record of the given ResultSet. - * - * @param context The thread context where the function is being executed. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @return true if all parameter are defined; false, otherwise. - * @throws DriverException if a parameter is not defined. - */ -bool sqlBooleanClausePreVerify(Context context, SQLBooleanClause* booleanClause); - -/** - * Evaluates the boolean clause, accordingly to values of the current record of the given ResultSet. - * - * @param resultSet the ResultSet used for the evaluation. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return 1, if the current record of the result set satisfies the boolean clause; 0 if the current record of the result set does not satisfy the - * boolean clause; -1, otherwise. - */ -int32 sqlBooleanClauseSatisfied(Context context, SQLBooleanClause* booleanClause, ResultSet* resultSet, Heap heap); - -/** - * Binds the column information of the underlying table list to the boolean clause. - * - * @param context The thread context where the function is being executed. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param names2Index IntHashtable that maps the column names to the column indexes. - * @param columnTypes The data types of each column. - * @param tableList The table list of the select clause. - * @param tableListSize The number of tables of the table list. - * @param heap A heap to allocate some new SQLBooleanClauseTree nodes. - * @return true, if the boolean clause was bound successfully; false, otherwise. - */ -bool bindColumnsSQLBooleanClause(Context context, SQLBooleanClause* booleanClause, Hashtable* names2Index, int8* columnTypes, - SQLResultSetTable** tableList, int32 tableListSize, Heap heap); - -/** - * Verifies if the column names are correct and belongs to the table list and is used only to verify if where clause and having clause field list - * is the field list of the where/having clause. - * - * @param context The thread context where the function is being executed. - * @param fieldList The field list of the where/having clause. - * @param fieldsCount The number of fields. - * @param tableList The table list. - * @param tableListSize The numbers of tables of the table list. - * @return true, if field name verification found no problems; false, otherwise. - * @throws SQLParseException If there is an unknown or an ambiguos column name. - */ -bool verifyColumnNamesOnTableList(Context context, SQLResultSetField** fieldList, int32 fieldsCount, SQLResultSetTable** tableList, - int32 tableListSize); - -/** - * Binds the column information of the underlying table to the boolean clause. - * - * @param context The thread context where the function is being executed. - * @param booleanClause A pointer to a SQLBooleanClause structure. - * @param rsTable The SQLResultSetTable table of the update or delete statement. - * @param heap A heap to allocate some new SQLBooleanClauseTree nodes. - * @return true, if the boolean clause was bound successfully; false, otherwise. - */ -bool bindColumnsSQLBooleanClauseSimple(Context context, SQLBooleanClause* clause, SQLResultSetTable* rsTable, Heap heap); - -/** - * Verifies if the column names are correct and belongs to the table and is used only to verify if where clause field list is the field list of - * the where clause. - * - * @param context The thread context where the function is being executed. - * @param fieldList The field list of the where/having clause. - * @param fieldsCount The number of fields. - * @param rsTable The SQLResultSetTable table of the update or delete statement. - * @return true, if field name verification found no problems; false, otherwise. - * @throws SQLParseException If there is an unknown or an ambiguos column name. - */ -bool verifyColumnNamesOnTable(Context context, SQLResultSetField** fieldList, int32 fieldsCount, SQLResultSetTable* rsTable); - -/** - * Validates a string value as a date or datetime according with its type. If it is well-formed, its value is transformed into one or two ints. - * - * @param context The thread context where the function is being executed. - * @param value The value that will receive the date or datetime as integers. - * @param valueType The expected type: date or datetime. - * @return true if the string storing a date or a datetime is well-formed; false, otherwise. - * @throws SQLParseException If the string is not well-formed. - */ -bool validateDateTime(Context context, SQLValue* value, int32 valueType); - -#endif diff --git a/LitebaseSDK/src/native/parser/SQLBooleanClauseTree.c b/LitebaseSDK/src/native/parser/SQLBooleanClauseTree.c deleted file mode 100644 index 11b0bbac18..0000000000 --- a/LitebaseSDK/src/native/parser/SQLBooleanClauseTree.c +++ /dev/null @@ -1,1496 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Internal use only. A tree structure used to evaluate a SQL boolean clause for a given result set. - */ - -#include "SQLBooleanClauseTree.h" - -/** - * Creates a SQLBooleanClauseTree with the associated SQLBooleanClause. - * - * @param booleanClause The associated SQLBooleanClause. - * @param heap The heap to allocate the SQLBooleanClauseTree structure. - * @return A pointer to a SQLBooleanClauseTree structure. - */ -SQLBooleanClauseTree* initSQLBooleanClauseTree(SQLBooleanClause* booleanClause, Heap heap) -{ - TRACE("initSQLBooleanClauseTree") - SQLBooleanClauseTree* tree = (SQLBooleanClauseTree*)TC_heapAlloc(heap, sizeof(SQLBooleanClauseTree)); - - tree->valueType = tree->indexRs = -1; - tree->booleanClause = booleanClause; - return tree; -} - -/** - * Sets the tree operand as a string literal. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The string literal value. - */ -void setOperandStringLiteral(SQLBooleanClauseTree* booleanClauseTree, JCharP value) -{ - TRACE("setOperandStringLiteral") - booleanClauseTree->valueType = CHARS_TYPE; - booleanClauseTree->operandValue.asChars = value; - booleanClauseTree->operandValue.length = TC_JCharPLen(value); - setPatternMatchType(booleanClauseTree); // Checks the pattern match type. -} - -// juliana@201_34 -/** - * Sets a numeric parameter value. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The numeric value to be set. - * @param type The type of the value. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the parameter type is different from the value type. - */ -bool setNumericParamValue(Context context, SQLBooleanClauseTree* booleanClauseTree, VoidP value, int32 type) -{ - TRACE("setNumericParamValue") - booleanClauseTree->isParamValueDefined = true; - if (booleanClauseTree->valueType == UNDEFINED_TYPE) - booleanClauseTree->valueType = type; - else if (booleanClauseTree->valueType != type) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return false; - } - switch (type) - { - case SHORT_TYPE: - booleanClauseTree->operandValue.asShort = *((int16*)value); - break; - case INT_TYPE: - booleanClauseTree->operandValue.asInt = *((int32*)value); - break; - case LONG_TYPE: - booleanClauseTree->operandValue.asLong = *((int64*)value); - break; - case FLOAT_TYPE: - booleanClauseTree->operandValue.asFloat = (float)*((double*)value); - break; - case DOUBLE_TYPE: - booleanClauseTree->operandValue.asDouble = *((double*)value); - } - return true; -} - -// juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. -/** - * Sets a string parameter value. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The string value to be set. - * @param len The length of the string. - * @throws SQLParseException If the value is not a valid number. - * @return false if an error occurs; true, otherwise. - */ -bool setParamValueString(Context context, SQLBooleanClauseTree* booleanClauseTree, JCharP value, int32 length) -{ - TRACE("setParamValueString") - DoubleBuf buffer; - bool error = false; - int32 type = booleanClauseTree->valueType; - SQLValue* operandValue = &booleanClauseTree->operandValue; - - booleanClauseTree->operandType = OP_STRING_LITERAL; - booleanClauseTree->isParamValueDefined = true; - - // juliana@223_4: Solved a bug that could make a prepared statement string parameter in a where or having clause cause a strange - // exception of invalid number. - // If the string has length greater than any type in string format, throws an exception. - if (type != CHARS_TYPE && type != CHARS_NOCASE_TYPE && type != UNDEFINED_TYPE) - { - if (length > 39) - { - CharP invalid = TC_JCharP2CharP(value, length); - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), invalid? invalid : "", "number"); - xfree(invalid); - return false; - } - TC_JCharP2CharPBuf(value, length, buffer); - } - operandValue->asChars = value; - operandValue->length = length; - - switch (type) // Converts the string to the correct type. - { - case SHORT_TYPE: - operandValue->asShort = str2short(buffer, &error); - break; - case INT_TYPE: - operandValue->asInt = TC_str2int(buffer, &error); - break; - case LONG_TYPE: - operandValue->asLong = TC_str2long(buffer, &error); - break; - case FLOAT_TYPE: - operandValue->asFloat = str2float(buffer, &error); - break; - case DOUBLE_TYPE: - operandValue->asDouble = TC_str2double(buffer, &error); - break; - case DATE_TYPE: // rnovais@570_55: If the type is DATE, checks if it is valid and converts it to int. - case DATETIME_TYPE: // If the type is DATETIME, checks if it is valid and converts it to 2 ints. - if (!testAndPrepareDateAndTime(context, operandValue, buffer, type)) - return false; - break; - - case UNDEFINED_TYPE: - booleanClauseTree->valueType = CHARS_TYPE; - } - - if (error) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), buffer, "number"); - return false; - } - else - setPatternMatchType(booleanClauseTree); // Checks the pattern match type. - return true; -} - -/** - * Checks the if the operand value string contains pattern matching characters and assigns the proper matching type. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - */ -void setPatternMatchType(SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("setPatternMatchType") - JCharP value = booleanClauseTree->operandValue.asChars; - int32 length = booleanClauseTree->operandValue.length; - - if (length > 0) // guich@512_5: checks if there is any pattern matching. - { - JChar firstChar = value[0], - lastChar = value[length - 1]; - - if (firstChar == PAT_MATCH_CHAR_ZERO_MORE) // '%...' - { - if (length == 1) // '%' // juliana@230_1: solved a bug with like %. - { - booleanClauseTree->patternMatchType = PAT_MATCH_ANYTHING; - booleanClauseTree->lenToMatch = 0; - } - else if (lastChar == PAT_MATCH_CHAR_ZERO_MORE) // '%...%' - { - booleanClauseTree->strToMatch = &value[1]; - booleanClauseTree->lenToMatch = length - 2; - booleanClauseTree->patternMatchType = PAT_MATCH_CONTAINS; - } - else // '%...' - { - booleanClauseTree->strToMatch = &value[1]; - booleanClauseTree->lenToMatch = length - 1; - booleanClauseTree->patternMatchType = PAT_MATCH_ENDS_WITH; - } - } - else if (lastChar == PAT_MATCH_CHAR_ZERO_MORE) // '...%' - { - booleanClauseTree->strToMatch = value; - booleanClauseTree->lenToMatch = length - 1; - booleanClauseTree->patternMatchType = PAT_MATCH_STARTS_WITH; - } - else // rnovais@568_1: accepts without % or % in the middle. - { - int32 pos = TC_JCharPIndexOfJChar(value, (JChar)'%', 0, length); - booleanClauseTree->strToMatch = value; - booleanClauseTree->lenToMatch = length; - - if (pos > 0) // In the middle. - { - booleanClauseTree->patternMatchType = PAT_MATCH_MIDDLE; - booleanClauseTree->posPercent = pos; - } - else // Without %. - booleanClauseTree->patternMatchType = PAT_MATCH_EQUAL; - } - } -} - -// juliana@238_2: improved join table reordering. -/** - * Weighs the tree to order the table on join operation. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - */ -void weightTheTree(SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("weightTheTree") - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - - switch (booleanClauseTree->operandType) // Checks the type of the operand. - { - // juliana@214_4: nots were removed. - - case OP_BOOLEAN_AND: - case OP_BOOLEAN_OR: - if (leftTree) - weightTheTree(leftTree); - if (rightTree) - weightTheTree(rightTree); - break; - - default: // The others. - { - SQLResultSetField** fieldList = booleanClauseTree->booleanClause->fieldList; - Hashtable* fieldName2Index = &booleanClauseTree->booleanClause->fieldName2Index; - SQLResultSetField* leftField = fieldList[TC_htGet32(fieldName2Index, leftTree->nameSqlFunctionHashCode)]; - SQLResultSetField* rightField = fieldList[TC_htGet32(fieldName2Index, rightTree->nameSqlFunctionHashCode)]; - Index* leftIndexStr = leftField->table->columnIndexes[leftField->tableColIndex]; - Index* rightIndexStr = rightField->table->columnIndexes[rightField->tableColIndex]; - - // field.indexRs is filled on the where clause validation. Both are identifiers. - if (leftTree->operandType == OP_IDENTIFIER && rightTree->operandType == OP_IDENTIFIER) - { - if (leftIndexStr) - leftField->table->weight++; - if (rightIndexStr) - rightField->table->weight++; - } - else if (leftTree->operandType == OP_IDENTIFIER) - { - if (leftIndexStr) - leftField->table->weight++; - } - else if (rightTree->operandType == OP_IDENTIFIER) - { - if (rightIndexStr) - rightField->table->weight++; - } - } - } -} - -/** - * Prepares the tree for the join operation. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - */ -void setIndexRsOnTree(SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("setIndexRsOnTree") - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - - switch (booleanClauseTree->operandType) // Checks the type of operand. - { - case OP_BOOLEAN_AND: - case OP_BOOLEAN_OR: - // juliana@214_4: nots were removed. - { - booleanClauseTree->indexRs = -1; - if (leftTree) - setIndexRsOnTree(leftTree); // Sets the indexRs on the left tree. - if (rightTree) - setIndexRsOnTree(rightTree); // Sets the indexRs on the right tree. - break; - } - case OP_REL_EQUAL: - case OP_REL_DIFF: - case OP_REL_GREATER: - case OP_REL_LESS: - case OP_REL_GREATER_EQUAL: - case OP_REL_LESS_EQUAL: - case OP_PAT_MATCH_LIKE: - case OP_PAT_MATCH_NOT_LIKE: - case OP_PAT_IS: - case OP_PAT_IS_NOT: - { - int32 leftIndex, - rightIndex, - fieldIndex; - SQLBooleanClause* booleanClause = booleanClauseTree->booleanClause; - SQLResultSetField** fieldList = booleanClause->fieldList; - - // field.indexRs is filled on the where clause validation. Both are identifier. - if (leftTree->operandType == OP_IDENTIFIER && rightTree->operandType == OP_IDENTIFIER) // Both are identifier. - { - // Puts the highest index on the indexRs. - if ((fieldIndex = getFieldIndex(leftTree)) < 0) - break; - leftIndex = fieldList[fieldIndex]->indexRs; - - if ((fieldIndex = getFieldIndex(rightTree)) < 0) - break; - rightIndex = fieldList[fieldIndex]->indexRs; - - leftTree->indexRs = leftIndex; - rightTree->indexRs = rightIndex; - - if (leftIndex > rightIndex) // Puts the least index on the left. - { - SQLBooleanClauseTree* auxTree = leftTree; - booleanClauseTree->leftTree = rightTree; - booleanClauseTree->rightTree = auxTree; - booleanClauseTree->indexRs = leftIndex; - } - else - booleanClauseTree->indexRs = rightIndex; - - // juliana@263_2: corrected a very old bug in a join with comparision between two fields of the same table. - if (leftIndex != rightIndex) - booleanClauseTree->bothAreIdentifier = true; - } - else if (leftTree->operandType == OP_IDENTIFIER) - { - if ((fieldIndex = getFieldIndex(leftTree)) < 0) - break; - booleanClauseTree->indexRs = fieldList[fieldIndex]->indexRs; - } - else if (rightTree->operandType == OP_IDENTIFIER) - { - if ((fieldIndex = getFieldIndex(rightTree)) < 0) - break; - booleanClauseTree->indexRs = fieldList[fieldIndex]->indexRs; - } - } - } -} - -/** - * Used for composed indices to find some properties related to a branch of the expression tree. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param columns The columns of the expression tree. - * @param operators The operators of the expression tree. - * @param indexesValueTree The part of the tree that uses indices. - * @param position The index of branch being analized. - * @param fieldsCount The number of fields of the boolean clause. - */ -void getBranchProperties(SQLBooleanClauseTree* booleanClauseTree, uint8* columns, uint8* operators, SQLBooleanClauseTree** indexesValueTree, - int32 position, int32 fieldsCount) -{ - TRACE("getBranchProperties") - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - - if (position >= fieldsCount) // Does not let an OutOfBoundsException. - return; - - if (leftTree->operandType == OP_IDENTIFIER) // One of the elements of the branch must be an identifier. - { - columns[position] = leftTree->colIndex; - indexesValueTree[position] = rightTree; - } - else if (rightTree->operandType == OP_IDENTIFIER) - { - columns[position] = rightTree->colIndex; - indexesValueTree[position] = leftTree; - } - else - { - columns[position] = -1; - return; - } - operators[position] = booleanClauseTree->operandType; -} - -/** - * Gets a value from a result set and applies a sql function if there is one to be applied. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The value returned from the result set or the result of a function on a value of the result set. - * @return true if the value could be fetched correctly; false, otherwise. - */ -bool getOperandValue(Context context, SQLBooleanClauseTree* booleanClauseTree, SQLValue* value) -{ - TRACE("getOperandValue") - - // If the operand type is identifier, gets the value from the current row in the result set. Otherwise, just returns the tree operand value. - if (booleanClauseTree->operandType == OP_IDENTIFIER) - { - SQLResultSetField* field; - SQLBooleanClause* booleanClause = booleanClauseTree->booleanClause; - ResultSet* resultSet = booleanClause->resultSet; - Table* table = resultSet->table; - - xmemmove(table->columnNulls, table->db.basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - if (isBitSet(table->columnNulls, booleanClauseTree->colIndex)) // There is a null value. - { - value->isNull = true; - return true; - } - - // rnovais@_570_1: created the last parameter for getTableColValue(). See this function. - if (!getTableColValue(context, resultSet, booleanClauseTree->colIndex, value)) - return false; - - // rnovais@568_10: applies data type function. - if ((field = booleanClause->fieldList[getFieldIndex(booleanClauseTree)])->sqlFunction != FUNCTION_DT_NONE) - applyDataTypeFunction(value, field->sqlFunction, field->parameter->dataType); - - value->isNull = false; - } - else - *value = booleanClauseTree->operandValue; - return true; -} - -/** - * Checks if an operand is null. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return true if the operand is null and a null value is being searched or vice-versa; false, otherwise. - */ -bool compareNullOperands(SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("compareNullOperands") - bool isNull; - Table* table = booleanClauseTree->booleanClause->resultSet->table; - xmemmove(table->columnNulls, table->db.basbuf + table->columnOffsets[table->columnCount], NUMBEROFBYTES(table->columnCount)); - isNull = isBitSet(table->columnNulls, booleanClauseTree->leftTree->colIndex); - return (booleanClauseTree->operandType == OP_PAT_IS)? isNull : !isNull; -} - -/** - * Compares two numerical operands. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return The evaluation of the comparison expression or -1 if an error occurs. - */ -int32 compareNumericOperands(Context context, SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("compareNumericOperands") - bool result = false; - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - SQLValue leftValue, - rightValue; - int32 leftValueType = leftTree->valueType, - rightValueType = rightTree->valueType, - assignType = booleanClauseTree->isFloatingPointType? DOUBLE_TYPE - : leftValueType != LONG_TYPE && rightValueType != LONG_TYPE && leftValueType != DATETIME_TYPE? INT_TYPE : LONG_TYPE, - compareType = leftValueType == DATETIME_TYPE? DATETIME_TYPE : assignType, - leftValueAsInt = 0, - rightValueAsInt = 0, - leftValueAsTime = 0, - rightValueAsTime = 0; - int64 leftValueAsLong = 0, - rightValueAsLong = 0; - double leftValueAsDouble = 0, - rightValueAsDouble = 0; - - // Gets the values. - if (booleanClauseTree->bothAreIdentifier) - leftValue = leftTree->valueJoin; - else - { - xmemzero(&leftValue, sizeof(SQLValue)); - if (!getOperandValue(context, leftTree, &leftValue)) - return -1; - } - xmemzero(&rightValue, sizeof(SQLValue)); - if (!getOperandValue(context, rightTree, &rightValue)) - return -1; - - if (leftValue.isNull || rightValue.isNull) // One of the values is a null value. - return false; - - switch (leftValueType) // Getting left value. - { - case SHORT_TYPE: - switch (assignType) - { - case DOUBLE_TYPE: - leftValueAsDouble = leftValue.asShort; - break; - case INT_TYPE: - leftValueAsInt = leftValue.asShort; - break; - case LONG_TYPE: - leftValueAsLong = leftValue.asShort; - } - break; - - case DATE_TYPE: // rnovais@567_2 - case INT_TYPE: - switch (assignType) - { - case DOUBLE_TYPE: - leftValueAsDouble = leftValue.asInt; - break; - case INT_TYPE: - leftValueAsInt = leftValue.asInt; - break; - case LONG_TYPE: - leftValueAsLong = leftValue.asInt; - } - break; - - case LONG_TYPE: - switch (assignType) - { - case DOUBLE_TYPE: - leftValueAsDouble = (double)leftValue.asLong; - break; - case LONG_TYPE: - leftValueAsLong = leftValue.asLong; - } - break; - - case FLOAT_TYPE: - leftValueAsDouble = leftValue.asFloat; - break; - - case DOUBLE_TYPE: - leftValueAsDouble = leftValue.asDouble; - break; - - case DATETIME_TYPE: // rnovais@567_2 - leftValueAsInt = leftValue.asDate; - leftValueAsTime = leftValue.asTime; - break; - } - - switch (rightValueType) // Getting right value. - { - case SHORT_TYPE: - switch (assignType) - { - case DOUBLE_TYPE: - rightValueAsDouble = rightValue.asShort; - break; - case INT_TYPE: - rightValueAsInt = rightValue.asShort; - break; - case LONG_TYPE: - rightValueAsLong = rightValue.asShort; - } - break; - - case INT_TYPE: - switch (assignType) - { - case DOUBLE_TYPE: - rightValueAsDouble = rightValue.asInt; - break; - case INT_TYPE: - rightValueAsInt = rightValue.asInt; - break; - case LONG_TYPE: - rightValueAsLong = rightValue.asInt; - } - break; - - case LONG_TYPE: - switch (assignType) - { - case DOUBLE_TYPE: - rightValueAsDouble = (double)rightValue.asLong; - break; - case LONG_TYPE: - rightValueAsLong = rightValue.asLong; - } - break; - - case FLOAT_TYPE: - rightValueAsDouble = rightValue.asFloat; - break; - - case DOUBLE_TYPE: - rightValueAsDouble = rightValue.asDouble; - break; - - case DATE_TYPE: // rnovais@570_55 - case DATETIME_TYPE: // rnovais@570_55 - case CHARS_TYPE: // rnovais@567_2 : this is for DATE and DATETIME typed that are CHARS type in the sql. - if (leftValueType == DATETIME_TYPE) - { - rightValueAsInt = rightValue.asDate; - rightValueAsTime = rightValue.asTime; - } - else - rightValueAsInt = rightValue.asInt; - } - - if (leftValueType == -1 || rightValueType == -1) // Prevents problems when doing comparisons like 1 == 2. - { - bool error; - DoubleBuf buffer; - JCharP leftValueAsString = leftValue.asChars, - rightValueAsString = rightValue.asChars; - uint32 last = TC_JCharPLen(leftValueAsString) - 1; - - // Strips off the final L, because str2double() does not like it. - if (leftValueAsString[last] == 'l' || leftValueAsString[last] == 'L') - leftValueAsString[last] = 0; - if (last >= sizeof(DoubleBuf)) // Prevents buffer overrun. - leftValueAsString[sizeof(DoubleBuf) - 1] = 0; - if (rightValueAsString[last = TC_JCharPLen(rightValueAsString) - 1] == 'l' || rightValueAsString[last] == 'L') - rightValueAsString[last] = 0; - if (last >= sizeof(DoubleBuf)) // Prevents buffer overrun. - rightValueAsString[sizeof(DoubleBuf) - 1] = 0; - - // juliana@225_12: a numeric constant in a boolean clause must have its type considered to be double. - TC_JCharP2CharPBuf(leftValueAsString, -1, buffer); - leftTree->operandValue.asDouble = leftValue.asDouble = leftValueAsDouble = TC_str2double(buffer, &error); - if (error) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), buffer, "double"); - return -1; - } - TC_JCharP2CharPBuf(rightValueAsString, -1, buffer); - rightTree->operandValue.asDouble = rightValue.asDouble = rightValueAsDouble = TC_str2double(buffer, &error); - if (error) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), buffer, "double"); - return -1; - } - - booleanClauseTree->leftTree->valueType = booleanClauseTree->rightTree->valueType = DOUBLE_TYPE; - booleanClauseTree->isFloatingPointType = true; - compareType = DOUBLE_TYPE; - } - - switch (booleanClauseTree->operandType) // Then perform the comparison. - { - case OP_REL_EQUAL: - case OP_REL_DIFF: - switch (compareType) - { - case DOUBLE_TYPE: - result = (leftValueAsDouble == rightValueAsDouble); - break; - case INT_TYPE: - result = (leftValueAsInt == rightValueAsInt); - break; - case LONG_TYPE: - result = (leftValueAsLong == rightValueAsLong); - break; - case DATETIME_TYPE: - result = (leftValueAsInt == rightValueAsInt) && (leftValueAsTime == rightValueAsTime); - } - if (booleanClauseTree->operandType == OP_REL_DIFF) - result = !result; - break; - - case OP_REL_GREATER: - case OP_REL_LESS_EQUAL: - switch (compareType) - { - case DOUBLE_TYPE: - result = (leftValueAsDouble > rightValueAsDouble); - break; - case INT_TYPE: - result = (leftValueAsInt > rightValueAsInt); - break; - case LONG_TYPE: - result = (leftValueAsLong > rightValueAsLong); - break; - case DATETIME_TYPE: - result = (leftValueAsInt == rightValueAsInt)? (leftValueAsTime > rightValueAsTime): (leftValueAsInt > rightValueAsInt); - } - if (booleanClauseTree->operandType == OP_REL_LESS_EQUAL) - result = !result; - break; - - case OP_REL_LESS: - case OP_REL_GREATER_EQUAL: - switch (compareType) - { - case DOUBLE_TYPE: - result = (leftValueAsDouble < rightValueAsDouble); - break; - case INT_TYPE: - result = (leftValueAsInt < rightValueAsInt); - break; - case LONG_TYPE: - result = (leftValueAsLong < rightValueAsLong); - break; - case DATETIME_TYPE: - result = (leftValueAsInt == rightValueAsInt)? (leftValueAsTime < rightValueAsTime): (leftValueAsInt < rightValueAsInt); - } - if (booleanClauseTree->operandType == OP_REL_GREATER_EQUAL) - result = !result; - } - return result; -} - -/** - * Compares two strings using LIKE and NOT LIKE. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param ignoreCase Indicates if both strings are CHARS_NOCASE. - * @return The evaluation of the comparison expression. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return The evaluation of the comparison expression or -1 if an error occurs. - */ -int32 matchStringOperands(Context context, SQLBooleanClauseTree* booleanClauseTree, bool ignoreCase, Heap heap) -{ - TRACE("matchStringOperands") - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - SQLValue leftValue = leftTree->valueJoin; - JCharP leftStringStr, - strToMatchStr = rightTree->strToMatch; - int32 leftStringLen, - strToMatchLen = rightTree->lenToMatch, - matchType = rightTree->patternMatchType, - pos, - strEndLen; - bool result = false; - JChar dateTimeBuf16[27]; - - if (!leftValue.asChars) - leftTree->valueJoin.asChars = leftValue.asChars = (JCharP)TC_heapAlloc(heap, (booleanClauseTree->booleanClause->resultSet->table->columnSizes[leftTree->colIndex] << 1) + 2); - if (!getOperandValue(context, leftTree, &leftValue)) - return -1; - if (leftValue.isNull) // null value - return false; // juliana@230_34: corrected possible wrong results when comparing with nulls. - - leftStringStr = leftValue.asChars; - leftStringLen = leftValue.length; - - // juliana@230_3: corrected a bug of LIKE using DATE and DATETIME not returning the correct result. - if (leftTree->valueType == DATE_TYPE) - { - int32 asDate = leftValue.asInt; - date2JCharP(asDate / 10000, asDate / 100 % 100, asDate % 100, leftStringStr = dateTimeBuf16); - leftStringLen = 10; - } - else if (leftTree->valueType == DATETIME_TYPE) - { - int32 asDate = leftValue.asDate, - asTime = leftValue.asTime; - dateTime2JCharP(asDate / 10000, asDate / 100 % 100, asDate % 100, - asTime / 10000000, asTime / 100000 % 100, asTime / 1000 % 100, asTime % 1000, leftStringStr = dateTimeBuf16); - leftStringLen = 23; - } - switch (matchType) - { - case PAT_MATCH_ANYTHING: - result = 1; - break; - - case PAT_MATCH_STARTS_WITH: - result = str16StartsWith(leftStringStr, strToMatchStr, leftStringLen, strToMatchLen, 0, ignoreCase); - break; - - case PAT_MATCH_ENDS_WITH: - result = str16StartsWith(leftStringStr, strToMatchStr, leftStringLen, strToMatchLen, leftStringLen - strToMatchLen, ignoreCase); - break; - - case PAT_MATCH_CONTAINS: - result = str16IndexOf(leftStringStr, strToMatchStr, leftStringLen, strToMatchLen, ignoreCase) >= 0; - break; - - case PAT_MATCH_MIDDLE: // rnovais@568_1 - strEndLen = strToMatchLen - (pos = rightTree->posPercent) - 1; - result = str16StartsWith(leftStringStr, strToMatchStr, leftStringLen, strToMatchLen = pos - 1, 0, ignoreCase)? - str16StartsWith(leftStringStr, &strToMatchStr[pos + 1], leftStringLen, strEndLen, leftStringLen - strEndLen, ignoreCase) : 0; - break; - - case PAT_MATCH_EQUAL: // rnovais@568_1 - if (ignoreCase) - result = TC_JCharPEqualsIgnoreCaseJCharP(leftStringStr, strToMatchStr, leftStringLen, strToMatchLen); - else - result = TC_JCharPEqualsJCharP(leftStringStr, strToMatchStr, leftStringLen, strToMatchLen); - } - - if (booleanClauseTree->operandType == OP_PAT_MATCH_NOT_LIKE) - result = !result; - - return result; -} - -/** - * Normal comparison between two strings. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param ignoreCase Indicates if both strings are CHARS_NOCASE. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return The evaluation of the comparison expression or -1 if an error occurs. - */ -int32 compareStringOperands(Context context, SQLBooleanClauseTree* booleanClauseTree, bool ignoreCase, Heap heap) -{ - TRACE("compareStringOperands") - bool result; - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - int32* columnSizes = booleanClauseTree->booleanClause->resultSet->table->columnSizes; - - SQLValue leftValue, - rightValue; - - if (!rightTree->valueJoin.asChars) - rightTree->valueJoin.asChars = (JCharP)TC_heapAlloc(heap, (columnSizes[rightTree->colIndex] << 1) + 2); - - rightValue = rightTree->valueJoin; - if (!getOperandValue(context, rightTree, &rightValue)) - return -1; - - if (!booleanClauseTree->bothAreIdentifier) - { - if (!leftTree->valueJoin.asChars && leftTree->operandType == OP_IDENTIFIER) - leftTree->valueJoin.asChars = (JCharP)TC_heapAlloc(heap, (columnSizes[leftTree->colIndex] << 1) + 2); - if (!getOperandValue(context, leftTree, &leftTree->valueJoin)) - return -1; - } - - leftValue = leftTree->valueJoin; - - if (leftValue.isNull || rightValue.isNull) // null value - return false; - - result = str16CompareTo(leftValue.asChars, rightValue.asChars, leftValue.length, rightValue.length, ignoreCase); - - switch (booleanClauseTree->operandType) - { - case OP_REL_EQUAL: - return !result; - case OP_REL_DIFF: - return result != 0; - case OP_REL_GREATER: - return result > 0; - case OP_REL_LESS: - return result < 0; - case OP_REL_GREATER_EQUAL: - return result >= 0; - case OP_REL_LESS_EQUAL: - return result <= 0; - } - return false; -} - -/** - * Evaluates an expression tree. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return The value of the expression evaluation or -1 if an error occurs. - */ -int32 booleanTreeEvaluate(Context context, SQLBooleanClauseTree* booleanClauseTree, Heap heap) -{ - TRACE("booleanTreeEvaluate") - int32 result = false; - - switch (booleanClauseTree->operandType) // Checks the operand type of the tree. - { - // Relational operantors. - case OP_REL_EQUAL: - case OP_REL_DIFF: - case OP_REL_GREATER: - case OP_REL_LESS: - case OP_REL_GREATER_EQUAL: - case OP_REL_LESS_EQUAL: - { - switch (booleanClauseTree->valueType) // Calls the right operation accordingly to the values type. - { - case SHORT_TYPE: - case INT_TYPE: - case LONG_TYPE: - case FLOAT_TYPE: - case DOUBLE_TYPE: - case DATE_TYPE: - case DATETIME_TYPE: - return compareNumericOperands(context, booleanClauseTree); - - case BLOB_TYPE: - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_COMP_BLOBS)); - return false; - - case CHARS_TYPE: - return compareStringOperands(context, booleanClauseTree, false, heap); - - case CHARS_NOCASE_TYPE: - return compareStringOperands(context, booleanClauseTree, true, heap); - } - return false; - } - - // LIKE operators. - case OP_PAT_MATCH_LIKE: - case OP_PAT_MATCH_NOT_LIKE: - return matchStringOperands(context, booleanClauseTree, booleanClauseTree->valueType == CHARS_NOCASE_TYPE, heap); - - case OP_BOOLEAN_AND: // AND connector. - { - // Expects both trees to be not null. - // Short circuit: only evaluates the right tree if left result is TRUE. - if (booleanClauseTree->leftTree && booleanClauseTree->rightTree && (result = booleanTreeEvaluate(context, booleanClauseTree->leftTree, heap)) == true) - return booleanTreeEvaluate(context, booleanClauseTree->rightTree, heap); - return result; - } - - case OP_BOOLEAN_OR: // OR connector. - { - // Expects both trees not be null. - if (booleanClauseTree->leftTree && booleanClauseTree->rightTree) // Expects both trees to be not null. - { - // Short circuit: only evaluates the right tree if the left result is FALSE. - if ((result = booleanTreeEvaluate(context, booleanClauseTree->leftTree, heap)) == true) - return true; - if (!result) - return booleanTreeEvaluate(context, booleanClauseTree->rightTree, heap); - return -1; - } - return false; - } - - // juliana@214_4: nots were removed. - - //rnovais@200_1 - case OP_PAT_IS: - case OP_PAT_IS_NOT: - return compareNullOperands(booleanClauseTree); - } - return false; -} - -/** - * Binds the column information of the underlying table to the boolean clause tree nodes. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return false If an exception is thrown; true, otherwise. - * @throws DriverException If there are imcompatible types or a column cannot be bound. - */ -bool bindColumnsSQLBooleanClauseTree(Context context, SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("bindColumnsSQLBooleanClauseTree") - SQLBooleanClauseTree* leftTree; - SQLBooleanClauseTree* rightTree; - - if (booleanClauseTree->operandType == OP_IDENTIFIER) // If operand type is identifier, binds to a column in the table. - { - int32 dtParameter, // rnovais@568_10 - fieldIndex = getFieldIndex(booleanClauseTree), // Stores the binding information also in the boolean clause field list. - colIndex; - SQLResultSetField* field; - SQLBooleanClause* booleanClause = booleanClauseTree->booleanClause; // Stores the binding information also in the boolean clause field list. - - if (fieldIndex < 0) // The column could not be found. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), booleanClauseTree->operandName); - return false; - } - - booleanClauseTree->colIndex = colIndex = (field = booleanClause->fieldList[fieldIndex])->tableColIndex; - - if (field->sqlFunction == FUNCTION_DT_NONE) // rnovais@568_10: if it does not have a data function, stores the correct value type. - { - dtParameter = field->dataType; - if (booleanClauseTree->valueType != UNDEFINED_TYPE && booleanClauseTree->valueType != dtParameter) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return false; - } - booleanClauseTree->valueType = dtParameter; - } - else - { - if (field->dataType == UNDEFINED_TYPE) // rnovais@570_5 - field->dataType = field->parameter->dataType; - dtParameter = field->dataType; - - if (booleanClauseTree->valueType != UNDEFINED_TYPE && booleanClauseTree->valueType != dtParameter) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return false; - } - booleanClauseTree->valueType = dtParameter; - } - - field->dataType = dtParameter; - } - - // Bind the columns of the children trees. - if ((leftTree = booleanClauseTree->leftTree) && !bindColumnsSQLBooleanClauseTree(context, leftTree)) - return false; - - if ((rightTree = booleanClauseTree->rightTree) && !bindColumnsSQLBooleanClauseTree(context, rightTree)) - return false; - - // Infers the operation resulting value type. - if (leftTree && rightTree && !inferOperationValueType(context, booleanClauseTree)) - return false; - - // rnovais@567_2: validates date and datetime in the rightTree. - if (leftTree && rightTree && (leftTree->valueType == DATE_TYPE || leftTree->valueType == DATETIME_TYPE)) - return validateDateTime(context, &rightTree->operandValue, leftTree->valueType); - return true; -} - -/** - * Infers the operation value type, according to the left and right values involved in the operation. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return false If an exception is thrown; true, otherwise. - * @throws SQLParseException If left and right values have incompatible types or there is a blob type in the comparison. - */ -bool inferOperationValueType(Context context, SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("inferOperationValueType") - SQLBooleanClauseTree* leftTree = booleanClauseTree->leftTree; - SQLBooleanClauseTree* rightTree = booleanClauseTree->rightTree; - int32 leftOperandType, - rightOperandType, - leftValueType, - rightValueType; - bool leftIsParameter, - rightIsParameter; - if (!leftTree || !rightTree) - { - booleanClauseTree->valueType = UNDEFINED_TYPE; - return true; - } - - if (rightTree->operandType == OP_PAT_NULL) - { - booleanClauseTree->valueType = leftTree->operandType; - return true; - } - - if (leftTree->operandName) // rnovais@568_10: if it has a data type function, verifies if it can be applied. - { - SQLBooleanClause* booleanClause = leftTree->booleanClause; - int32 fieldIndex = getFieldIndex(leftTree); - SQLResultSetField* field = booleanClause->fieldList[fieldIndex]; - - if (field->sqlFunction != FUNCTION_DT_NONE && !bindFunctionDataType(field->parameter->dataType, field->sqlFunction)) // Incompatible function. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DATA_TYPE_FUNCTION), dataTypeFunctionsName(field->sqlFunction)); - return false; - } - } - - if (booleanClauseTree->operandType == OP_BOOLEAN_AND || booleanClauseTree->operandType == OP_BOOLEAN_OR) // Boolean type. - { - booleanClauseTree->valueType = BOOLEAN_TYPE; - return true; - } - - leftOperandType = leftTree->operandType; - rightOperandType = rightTree->operandType; - leftValueType = leftTree->valueType; - rightValueType = rightTree->valueType; - leftIsParameter = leftTree->isParameter; - rightIsParameter = rightTree->isParameter; - - if (leftValueType == BLOB_TYPE || rightValueType == BLOB_TYPE) // Blobs can't be compared. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_COMP_BLOBS)); - return false; - } - - // In case one of them is a parameter, the tree has the type of the one that is not a parameter. - // If both are parameters, the type is undefined (which is the default, anyway). - if (leftIsParameter || rightIsParameter) - { - if (leftIsParameter) - booleanClauseTree->valueType = leftTree->valueType = rightValueType; - else - booleanClauseTree->valueType = rightTree->valueType = leftValueType; - } - else - { - // rnovais@567_2: date and dateTime are string values in a sql statement. - bool leftIsChar = (leftValueType == CHARS_TYPE || leftValueType == CHARS_NOCASE_TYPE - || leftValueType == DATE_TYPE || leftValueType == DATETIME_TYPE), - - // juliana@201_12: both should be compared to DATE and DATETIME. - rightIsChar = (rightValueType == CHARS_TYPE || rightValueType == CHARS_NOCASE_TYPE - || leftValueType == DATE_TYPE || leftValueType == DATETIME_TYPE); - - - if (leftIsChar != rightIsChar) // Can not mix a character type with a non-character type, except for DATE and DATETIME. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return false; - } - - if ((leftValueType == DATE_TYPE || leftValueType == DATETIME_TYPE) && rightValueType == CHARS_TYPE) // rnovais@567_2 - rightValueType = leftValueType; - - // If one of the operands is an identifier and the other one is not, it prevails the identifier operand value type. - if (rightOperandType == OP_IDENTIFIER && leftOperandType != OP_IDENTIFIER) - { - if (!convertValue(context, booleanClauseTree->leftTree, booleanClauseTree->valueType = rightValueType)) - return false; - } - else if (leftOperandType == OP_IDENTIFIER && rightOperandType != OP_IDENTIFIER) - { - if (!convertValue(context, booleanClauseTree->rightTree, booleanClauseTree->valueType = leftValueType)) - return false; - } - else switch (booleanClauseTree->operandType) - { - // Relational operators. - case OP_REL_EQUAL: - case OP_REL_DIFF: - case OP_REL_GREATER: - case OP_REL_LESS: - case OP_REL_GREATER_EQUAL: - case OP_REL_LESS_EQUAL: - if (leftIsChar || rightIsChar) - { - if (leftValueType == DATE_TYPE || rightValueType == DATE_TYPE) // rnovais@567_2 - booleanClauseTree->valueType = DATE_TYPE; - else if (leftValueType == DATETIME_TYPE || rightValueType == DATETIME_TYPE) - booleanClauseTree->valueType = DATETIME_TYPE; - else - { - // If both are identifiers, it prevails CHARS, in case one of them is CHARS (case sensitive). - if (leftOperandType == OP_IDENTIFIER && rightOperandType == OP_IDENTIFIER) - { - if (leftValueType == CHARS_TYPE || rightValueType == CHARS_TYPE) - booleanClauseTree->valueType = CHARS_TYPE; - else - booleanClauseTree->valueType = CHARS_NOCASE_TYPE; - } - else - booleanClauseTree->valueType = CHARS_TYPE; - } - } - else - { - // This order is important. - if (leftValueType == DOUBLE_TYPE || rightValueType == DOUBLE_TYPE) - booleanClauseTree->valueType = DOUBLE_TYPE; - else if (leftValueType == FLOAT_TYPE || rightValueType == FLOAT_TYPE) - booleanClauseTree->valueType = FLOAT_TYPE; - else if (leftValueType == LONG_TYPE || rightValueType == LONG_TYPE) - booleanClauseTree->valueType = LONG_TYPE; - else if (leftValueType == INT_TYPE || rightValueType == INT_TYPE) - booleanClauseTree->valueType = INT_TYPE; - else - booleanClauseTree->valueType = SHORT_TYPE; - } - break; - - // Like operators. - case OP_PAT_MATCH_LIKE: - case OP_PAT_MATCH_NOT_LIKE: - if (!leftIsChar || !rightIsChar) // Only character types are allowed here. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INCOMPATIBLE_TYPES)); - return false; - } - - if (leftValueType == DATE_TYPE || rightValueType == DATE_TYPE) // rnovais@567_2 - booleanClauseTree->valueType = DATE_TYPE; - else if (leftValueType == DATETIME_TYPE || rightValueType == DATETIME_TYPE) - booleanClauseTree->valueType = DATETIME_TYPE; - else - { - // If both are identifiers, it prevails CHARS, in case one of them is CHARS (case sensitive). - if (leftOperandType == OP_IDENTIFIER && rightOperandType == OP_IDENTIFIER) - { - if (leftValueType == CHARS_TYPE || rightValueType == CHARS_TYPE) - booleanClauseTree->valueType = CHARS_TYPE; - else - booleanClauseTree->valueType = CHARS_NOCASE_TYPE; - } - else - booleanClauseTree->valueType = CHARS_TYPE; - } - - break; - - default: - booleanClauseTree->valueType = UNDEFINED_TYPE; - } - } - - // Checks if the operand value type has floating point. - booleanClauseTree->isFloatingPointType = booleanClauseTree->valueType == DOUBLE_TYPE || booleanClauseTree->valueType == FLOAT_TYPE; - return true; -} - -// juliana@222_9: Some string conversions to numerical values could return spourious values if the string range were greater than the type range. -/** - * Converts a number in the string format to its numerical representation. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param type The number type. - * @throws SQLParseException If the value to be converted is not a valid number. - */ -bool convertValue(Context context, SQLBooleanClauseTree* booleanClauseTree, int32 type) -{ - TRACE("convertValue") - DoubleBuf buffer; // Widest type. - SQLValue* operandValue = &booleanClauseTree->operandValue; - bool error = false; - - if (type < SHORT_TYPE || type > DOUBLE_TYPE) // juliana@201_10: non-numerical types don't need to be converted to numbers. - return true; - - if ((operandValue->length = TC_JCharPLen(operandValue->asChars)) > 39) - { - CharP invalid = TC_JCharP2CharP(operandValue->asChars, operandValue->length); - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), invalid? invalid : "", "number"); - xfree(invalid); - return false; - } - - TC_JCharP2CharPBuf(operandValue->asChars, -1, buffer); - - switch (type) - { - case SHORT_TYPE: - operandValue->asShort = str2short(buffer, &error); - booleanClauseTree->valueType = SHORT_TYPE; - break; - case INT_TYPE: - operandValue->asInt = TC_str2int(buffer, &error); - booleanClauseTree->valueType = INT_TYPE; - break; - case LONG_TYPE: - operandValue->asLong = TC_str2long(buffer, &error); - booleanClauseTree->valueType = LONG_TYPE; - break; - case FLOAT_TYPE: - operandValue->asFloat = str2float(buffer, &error); - booleanClauseTree->valueType = FLOAT_TYPE; - break; - case DOUBLE_TYPE: - operandValue->asDouble = TC_str2double(buffer, &error); - booleanClauseTree->valueType = DOUBLE_TYPE; - } - - if (error) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_INVALID_NUMBER), buffer, "number"); - return false; - } - return true; -} - -// juliana@214_4: removes not from expression trees so that indices can be used in more situations. -/** - * Removes the not operators from an expression tree. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param heap The heap to allocate a SQLBooleanClauseTree node. - * @return The expression tree without nots. - */ -SQLBooleanClauseTree* removeNots(SQLBooleanClauseTree* booleanClauseTree, Heap heap) -{ - TRACE("removeNots") - SQLBooleanClauseTree* tree; - SQLBooleanClauseTree* rightTree; - - if (!booleanClauseTree) // Does nothing with an empty expression is used. - return null; - - rightTree = booleanClauseTree->rightTree; - - if (booleanClauseTree->operandType == OP_BOOLEAN_NOT) - { - switch (rightTree->operandType) - { - case OP_REL_EQUAL: // not equal == dif. - rightTree->operandType = OP_REL_DIFF; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_REL_DIFF: // not dif == equal. - rightTree->operandType = OP_REL_EQUAL; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_REL_GREATER: // not greater == less equal. - rightTree->operandType = OP_REL_LESS_EQUAL; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_REL_LESS: // not less == greater equal. - rightTree->operandType = OP_REL_GREATER_EQUAL; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_REL_GREATER_EQUAL: // not greater equal == less. - rightTree->operandType = OP_REL_LESS; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_REL_LESS_EQUAL: // not less equal == greates. - rightTree->operandType = OP_REL_GREATER; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_PAT_IS: // not is == is not. - rightTree->operandType = OP_PAT_IS_NOT; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_PAT_IS_NOT: // not is not == is. - rightTree->operandType = OP_PAT_IS; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_PAT_MATCH_LIKE: // not like == not like. - rightTree->operandType = OP_PAT_MATCH_NOT_LIKE; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_PAT_MATCH_NOT_LIKE: // not not like == like. - rightTree->operandType = OP_PAT_MATCH_LIKE; - rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree; - break; - case OP_BOOLEAN_NOT: // not not == null. - rightTree->rightTree->parent = booleanClauseTree->parent; - booleanClauseTree = rightTree->rightTree; - break; - case OP_BOOLEAN_AND: // not (A and B) == not A or not B. - tree = initSQLBooleanClauseTree(booleanClauseTree->booleanClause, heap); - tree->operandType = OP_BOOLEAN_OR; - tree->leftTree = booleanClauseTree; - tree->rightTree = rightTree; - tree->rightTree->operandType = OP_BOOLEAN_NOT; - booleanClauseTree->parent = tree->rightTree->parent = tree; - booleanClauseTree->rightTree = tree->rightTree->leftTree; - tree->rightTree->leftTree = null; - rightTree->parent = booleanClauseTree; - booleanClauseTree = tree; - break; - case OP_BOOLEAN_OR: // not (A or B) == not A and not B. - tree = initSQLBooleanClauseTree(booleanClauseTree->booleanClause, heap); - tree->operandType = OP_BOOLEAN_AND; - tree->leftTree = booleanClauseTree; - tree->rightTree = rightTree; - tree->rightTree->operandType = OP_BOOLEAN_NOT; - booleanClauseTree->parent = tree->rightTree->parent = tree; - booleanClauseTree->rightTree = tree->rightTree->leftTree; - tree->rightTree->leftTree = null; - rightTree->parent = booleanClauseTree; - booleanClauseTree = tree; - } - } - - // Recursion. - booleanClauseTree->leftTree = removeNots(booleanClauseTree->leftTree, heap); - booleanClauseTree->rightTree = removeNots(booleanClauseTree->rightTree, heap); - - return booleanClauseTree; -} - -/** - * Gets the field index given the hash code of the SQL function used or the field if there is no function applied. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return The index of the field used by this branch of the tree. - */ -int32 getFieldIndex(SQLBooleanClauseTree* booleanClauseTree) -{ - TRACE("getFieldIndex") - return (TC_htGet32Inv(&(booleanClauseTree)->booleanClause->fieldName2Index, (booleanClauseTree)->nameSqlFunctionHashCode? (booleanClauseTree)->nameSqlFunctionHashCode : (booleanClauseTree)->nameHashCode)); -} - -/** - * Validates a string value as a date or datetime according with its type. If it is well-formed, its value is transformed into one or two ints. - * - * @param context The thread context where the function is being executed. - * @param value The value that will receive the date or datetime as integers. - * @param valueType The expected type: date or datetime. - * @return true if the string storing a date or a datetime is well-formed; false, otherwise. - * @throws SQLParseException If the string is not well-formed. - */ -bool validateDateTime(Context context, SQLValue* value, int32 valueType) -{ - TRACE("validateDateTime") - int32 length = value->length; - DateTimeBuf buffer; - - // First, converts and trims. - if (!value->asChars) - return true; - - if (!length) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_VALUE_ISNOT_DATETIME), ""); - return false; - } - if (length >= 27) - { - CharP str = TC_JCharP2CharP(value->asChars, length); - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_VALUE_ISNOT_DATETIME), (str? str : "")); - xfree(str); - return false; - } - - TC_JCharP2CharPBuf(value->asChars, length, buffer); - if (xstrchr(buffer,'%')) // A % is used in a like operation. - return true; - - return testAndPrepareDateAndTime(context, value, buffer, valueType); -} - -// juliana@226_15: corrected a bug that would make a prepared statement with where clause and indices not work correctly after the first execution. -/** - * Clones an expression tree of a where clause of a select prepared statement. - * - * @param booleanClauseTree The expression tree to be cloned. - * @param destTree The old destination tree. If the nodes are the same of the tree cloned, the node is reused in the cloned tree. - * @param heap A heap to allocate the new tree. - * @return A clone of the where clause expression tree. - */ -SQLBooleanClauseTree* cloneTree(SQLBooleanClauseTree* booleanClauseTree, SQLBooleanClauseTree* destTree, Heap heap) -{ - TRACE("cloneTree") - SQLBooleanClause* booleanClause = booleanClauseTree->booleanClause; - SQLBooleanClauseTree** paramList = booleanClause->paramList; - SQLBooleanClauseTree* tree = null; - int32 i = booleanClause->paramCount; - - while (--i >= 0) - if (booleanClauseTree == paramList[i]) - { - tree = booleanClauseTree; - break; - } - if (!tree) - { - if (destTree != booleanClauseTree) - { - (tree = initSQLBooleanClauseTree(booleanClause, heap))->booleanClause = booleanClause; - tree->bothAreIdentifier = booleanClauseTree->bothAreIdentifier; - tree->colIndex = booleanClauseTree->colIndex; - tree->hasIndex = booleanClauseTree->hasIndex; - tree->indexRs = booleanClauseTree->indexRs; - tree->isFloatingPointType = booleanClauseTree->isFloatingPointType; - tree->isParameter = booleanClauseTree->isParameter; - tree->isParamValueDefined = booleanClauseTree->isParamValueDefined; - tree->nameHashCode = booleanClauseTree->nameHashCode; - tree->nameSqlFunctionHashCode = booleanClauseTree->nameSqlFunctionHashCode; - tree->operandName = booleanClauseTree->operandName; - tree->operandType = booleanClauseTree->operandType; - tree->operandValue = booleanClauseTree->operandValue; - tree->patternMatchType = booleanClauseTree->patternMatchType; - tree->posPercent = booleanClauseTree->posPercent; - tree->strToMatch = booleanClauseTree->strToMatch; - - // juliana@227_8: Solved a bug on select prepared statement with like not returning the correct result. - tree->lenToMatch = booleanClauseTree->lenToMatch; - - tree->valueType = booleanClauseTree->valueType; - } - else - tree = destTree; - } - if (booleanClauseTree->leftTree) - (tree->leftTree = cloneTree(booleanClauseTree->leftTree, destTree? destTree->leftTree : null, heap))->parent = tree; - if (booleanClauseTree->rightTree) - (tree->rightTree = cloneTree(booleanClauseTree->rightTree, destTree? destTree->rightTree : null, heap))->parent = tree; - return tree; -} diff --git a/LitebaseSDK/src/native/parser/SQLBooleanClauseTree.h b/LitebaseSDK/src/native/parser/SQLBooleanClauseTree.h deleted file mode 100644 index 57b34c58a9..0000000000 --- a/LitebaseSDK/src/native/parser/SQLBooleanClauseTree.h +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Internal use only. A tree structure used to evaluate a SQL boolean clause for a given result set. - */ - -#ifndef SQLBOOLEANCLAUSETREE_H -#define SQLBOOLEANCLAUSETREE_H - -#include "Litebase.h" - -/** - * Creates a SQLBooleanClauseTree with the associated SQLBooleanClause. - * - * @param booleanClause The associated SQLBooleanClause. - * @param heap The heap to allocate the SQLBooleanClauseTree structure. - * @return A pointer to a SQLBooleanClauseTree structure. - */ -SQLBooleanClauseTree* initSQLBooleanClauseTree(SQLBooleanClause* booleanClause, Heap heap); - -/** - * Sets the tree operand as a string literal. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The string literal value. - */ -void setOperandStringLiteral(SQLBooleanClauseTree* booleanClauseTree, JCharP value); - -/** - * Sets a numeric parameter value. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The numeric value to be set. - * @param type The type of the value. - * @return false if an error occurs; true, otherwise. - * @throws DriverException If the parameter type is different from the value type. - */ -bool setNumericParamValue(Context context, SQLBooleanClauseTree* booleanClauseTree, VoidP value, int32 type); - -/** - * Sets a string parameter value. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The string value to be set. - * @param len The length of the string. - * @throws SQLParseException If the value is not a valid number. - * @return false if an error occurs; true, otherwise. - */ -bool setParamValueString(Context context, SQLBooleanClauseTree* booleanClauseTree, JCharP value, int32 length); - -/** - * Checks the if the operand value string contains pattern matching characters and assigns the proper matching type. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - */ -void setPatternMatchType(SQLBooleanClauseTree* booleanClauseTree); - -/** - * Weighs the tree to order the table on join operation. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - */ -void weightTheTree(SQLBooleanClauseTree* booleanClauseTree); - -/** - * Prepares the tree for the join operation. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - */ -void setIndexRsOnTree(SQLBooleanClauseTree* booleanClauseTree); - -/** - * Used for composed indices to find some properties related to a branch of the expression tree. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param columns The columns of the expression tree. - * @param operators The operators of the expression tree. - * @param indexesValueTree The part of the tree that uses indices. - * @param position The index of branch being analized. - * @param fieldsCount The number of fields of the boolean clause. - */ -void getBranchProperties(SQLBooleanClauseTree* booleanClauseTree, uint8* columns, uint8* operators, SQLBooleanClauseTree** indexesValueTree, - int32 position, int32 fieldsCount); -/** - * Gets a value from a result set and applies a sql function if there is one to be applied. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param value The value returned from the result set or the result of a function on a value of the result set. - * @return true if the value could be fetched correctly; false, otherwise. - */ -bool getOperandValue(Context context, SQLBooleanClauseTree* booleanClauseTree, SQLValue* value); - -/** - * Checks if an operand is null. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return true if the operand is null and a null value is being searched or vice-versa; false, otherwise. - */ -bool compareNullOperands(SQLBooleanClauseTree* booleanClauseTree); - -/** - * Compares two numerical operands. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return The evaluation of the comparison expression or -1 if an error occurs. - */ -int32 compareNumericOperands(Context context, SQLBooleanClauseTree* booleanClauseTree); - -/** - * Compares two strings using LIKE and NOT LIKE. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param ignoreCase Indicates if both strings are CHARS_NOCASE. - * @return The evaluation of the comparison expression. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return The evaluation of the comparison expression or -1 if an error occurs. - */ -int32 matchStringOperands(Context context, SQLBooleanClauseTree* booleanClauseTree, bool ignoreCase, Heap heap); - -/** - * Normal comparison between two strings. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param ignoreCase Indicates if both strings are CHARS_NOCASE. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return The evaluation of the comparison expression or -1 if an error occurs. - */ -int32 compareStringOperands(Context context, SQLBooleanClauseTree* booleanClauseTree, bool ignoreCase, Heap heap); - -/** - * Evaluates an expression tree. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param heap A heap to alocate temporary strings in the expression tree. - * @return The value of the expression evaluation or -1 if an error occurs. - */ -int32 booleanTreeEvaluate(Context context, SQLBooleanClauseTree* booleanClauseTree, Heap heap); - -/** - * Binds the column information of the underlying table to the boolean clause tree nodes. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return false If an exception is thrown; true, otherwise. - * @throws DriverException If there are imcompatible types or a column cannot be bound. - */ -bool bindColumnsSQLBooleanClauseTree(Context context, SQLBooleanClauseTree* booleanClauseTree); - -/** - * Infers the operation value type, according to the left and right values involved in the operation. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return false If an exception is thrown; true, otherwise. - * @throws SQLParseException If left and right values have incompatible types or there is a blob type in the comparison. - */ -bool inferOperationValueType(Context context, SQLBooleanClauseTree* booleanClauseTree); - -/** - * Converts a number in the string format to its numerical representation. - * - * @param context The thread context where the function is being executed. - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param type The number type. - * @throws SQLParseException If the value to be converted is not a valid number. - */ -bool convertValue(Context context, SQLBooleanClauseTree* booleanClauseTree, int32 type); - -// juliana@214_4: removes not from expression trees so that indices can be used in more situations. -/** - * Removes the not operators from an expression tree. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @param heap The heap to allocate a SQLBooleanClauseTree node. - * @return The expression tree without nots. - */ -SQLBooleanClauseTree* removeNots(SQLBooleanClauseTree* booleanClauseTree, Heap heap); - -/** - * Gets the field index given the hash code of the SQL function used or the field if there is no function applied. - * - * @param booleanClauseTree A pointer to a SQLBooleanClauseTree structure. - * @return The index of the field used by this branch of the tree. - */ -int32 getFieldIndex(SQLBooleanClauseTree* booleanClauseTree); - -/** - * Clones an expression tree of a where clause of a select prepared statement. - * - * @param booleanClauseTree The expression tree to be cloned. - * @param destTree The old destination tree. If the nodes are the same of the tree cloned, the node is reused in the cloned tree. - * @param heap A heap to allocate the new tree. - * @return A clone of the where clause expression tree. - */ -SQLBooleanClauseTree* cloneTree(SQLBooleanClauseTree* booleanClauseTree, SQLBooleanClauseTree* destTree, Heap heap); - -#endif diff --git a/LitebaseSDK/src/native/parser/SQLColumnListClause.c b/LitebaseSDK/src/native/parser/SQLColumnListClause.c deleted file mode 100644 index 05438437d2..0000000000 --- a/LitebaseSDK/src/native/parser/SQLColumnListClause.c +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines functions to deal with a SQL column list clause, like order by or group by. - */ - -#include "SQLColumnListClause.h" - -/** - * Compares two SQL column lists clauses. They can only be considered equal if they list the same column list in the same sequence. - * - * @param clause1 The first list used in the comparison. - * @param clause1 The second list used in the comparison. - * @return true, if both column lists list the same column sequence; false, otherwise. - */ -bool sqlcolumnlistclauseEquals(SQLColumnListClause* clause1, SQLColumnListClause* clause2) -{ - TRACE("sqlcolumnlistclauseEquals") - int32 length = clause1->fieldsCount; // the length of the first column list. - SQLResultSetField** fieldList1 = clause1->fieldList; - SQLResultSetField** fieldList2 = clause2->fieldList; - - if (length != clause2->fieldsCount) // If the length of the column lists are different, the lists are different. - return false; - - // If a field of one list has an index of a column different of the same member of the other list or they don�t match concerning the ordering - // of the result, the lists are considered to be different. - while (--length >= 0) - if ((*fieldList1++)->tableColIndex != (*fieldList2++)->tableColIndex) - return false; - return true; -} - -/** - * Checks if the column list contains the given column. - * - * @param clause The column list clause. - * @param colIndex The column index of the column being searched for. - * @return true if the column is in the column list clause; false, otherwise. - */ -bool sqlcolumnlistclauseContains(SQLColumnListClause* clause, int32 colIndex) -{ - TRACE("sqlcolumnlistclauseContains") - int32 i = clause->fieldsCount; - SQLResultSetField** fieldList = clause->fieldList; - - while (--i >= 0) - if ((*fieldList++)->tableColIndex == colIndex) - return true; - - return false; -} - -/** - * Binds the column information of the underlying order or group by clause to the select clause. - * - * @param context The thread context where the function is being executed. - * @param clause The column list clause. - * @param names2Index The select clause columns hash table. - * @param columTypes The select clause tables column types. - * @param tableList The select clause tables. - * @param tableListSize The number of tables of the select clause. - * @throws SQLParseException If the column in a group or order by clause is not in the select clause or there is a column of type blob in the - * clause. - */ -bool bindColumnsSQLColumnListClause(Context context, SQLColumnListClause* clause, Hashtable* names2Index, int8* columnTypes, - SQLResultSetTable** tableList, int32 tableListSize) -{ - TRACE("bindColumnsSQLColumnListClause") - int32 i = clause->fieldsCount, - index = -1; - SQLResultSetField** fieldList = clause->fieldList; - SQLResultSetField* field; - - if (tableList) // Binds before executing the query. - { - SQLResultSetTable* rsTable; - int32 j; - - while (--i >= 0) - { - field = fieldList[i]; - j = tableListSize; - - while (--j >=0) - { - index = TC_htGet32Inv(&(rsTable = tableList[j])->table->htName2index, field->tableColHashCode); - if (xstrchr(field->alias, '.') && xstrncmp(field->alias, rsTable->aliasTableName, xstrlen(rsTable->aliasTableName))) - index = -1; - if (index >= 0) - { - field->table = rsTable->table; - break; - } - } - - if (index < 0) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), field->tableColName); - return false; - } - - if ((field->dataType = (int8)columnTypes[field->tableColIndex = index]) == BLOB_TYPE) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_BLOB_ORDER_GROUP)); - return false; - } - } - } - else // Bind during a sorting. - { - while (--i >= 0) - { - index = TC_htGet32Inv(names2Index, (field = fieldList[i])->tableColHashCode); - field->dataType = (int8)columnTypes[field->tableColIndex = index]; - } - } - - return true; -} - -// juliana@230_29: order by and group by now use indices on simple queries. -/** - * Finds the best index to use in a sort operation. - * - * @param clause An order or group by clause. - */ -void findSortIndex(SQLColumnListClause* clause) -{ - TRACE("findSortIndex") - int32 length = clause->fieldsCount; - SQLResultSetField** fieldList = clause->fieldList; - SQLResultSetField* field = fieldList[0]; - Table* table = field->table; - - clause->index = -1; - if (length == 1) - { - // To use an index for ordering, it must use only non-null columns because Litebase indices don't store nulls. - // If there is only one field and it is a primary key, uses the primary key index (it is not null). - if (table->primaryKeyCol == field->tableColIndex) - { - clause->index = field->tableColIndex; - clause->isComposed = false; - } - - // If it is another not null field, try to find an index for it. - else if ((table->columnAttrs[field->tableColIndex] & ATTR_COLUMN_IS_NOT_NULL)) - { - findMaxMinIndex(field); - clause->index = field->index; - clause->isComposed = field->isComposed; - } - } - else - { - bool isAscending = field->isAscending, - areAllNotNull = true; - int32 i = -1, - j; - uint8* composedPKCols = table->composedPrimaryKeyCols; - ComposedIndex** compIndices = table->composedIndexes; - ComposedIndex* compIndex; - - while (--length > 0) - { - // All the fields to be sorted must have the same table and ordering. - if ((field = fieldList[length])->isAscending != isAscending || field->table != table) - return; - - // To use an index for ordering, it must use only non-null columns because Litebase indices don't store nulls. - if ((table->columnAttrs[field->tableColIndex] & ATTR_COLUMN_IS_NOT_NULL)) - areAllNotNull = false; - } - - // Checks if the fields to be sorted are the first part of the composed PK. - if (table->numberComposedPKCols >= (length = clause->fieldsCount)) - { - while (++i < length) - if (composedPKCols[i] != fieldList[i]->tableColIndex) - break; - - if (i == length) // If so, the composed PK can be used (it is not null). - { - clause->index = table->composedPK; - clause->isComposed = true; - return; - } - - } - - // If the fields to be sorted are not part of a composed PK and are not all not null, does not use an index. - if (!areAllNotNull) - return; - - // If they are all not null, it is necessary to find a composed index for them. - i = table->numberComposedIndexes; - while (--i >= 0) - { - // Checks if the fields to be sorted are the first part of the composed index. - if ((compIndex = compIndices[i])->numberColumns >= length) - { - j = -1; - while (++j < length) - if (compIndex->columns[j] != fieldList[j]->tableColIndex) - break; - - if (j == length) // If so, the composed PK can be used (it is not null). - { - clause->index = i; - clause->isComposed = true; - return; - } - } - } - } -} diff --git a/LitebaseSDK/src/native/parser/SQLColumnListClause.h b/LitebaseSDK/src/native/parser/SQLColumnListClause.h deleted file mode 100644 index 0c83511ae6..0000000000 --- a/LitebaseSDK/src/native/parser/SQLColumnListClause.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares functions to deal with a SQL column list clause, like order by or group by. - */ - -#ifndef LITEBASE_SQLCOLUMNLISTCLAUSE_H -#define LITEBASE_SQLCOLUMNLISTCLAUSE_H - -#include "Litebase.h" - -/** - * Compares two SQL column lists clauses. They can only be considered equal if they list the same column list in the same sequence. - * - * @param clause1 The first list used in the comparison. - * @param clause1 The second list used in the comparison. - * @return true, if both column lists list the same column sequence; false, otherwise. - */ -bool sqlcolumnlistclauseEquals(SQLColumnListClause* clause1, SQLColumnListClause* clause2); - -/** - * Checks if the column list contains the given column. - * - * @param clause The column list clause. - * @param colIndex The column index of the column being searched for. - * @return true if the column is in the column list clause; false, otherwise. - */ -bool sqlcolumnlistclauseContains(SQLColumnListClause* clause, int32 colIndex); - -/** - * Binds the column information of the underlying order or group by clause to the select clause. - * - * @param context The thread context where the function is being executed. - * @param clause The column list clause. - * @param names2Index The select clause columns hash table. - * @param columTypes The select clause tables column types. - * @param tableList The select clause tables. - * @param tableListSize The number of tables of the select clause. - * @throws SQLParseException If the column in a group or order by clause is not in the select clause or there is a column of type blob in the - * clause. - */ -bool bindColumnsSQLColumnListClause(Context context, SQLColumnListClause* clause, Hashtable* names2Index, int8* columnTypes, - SQLResultSetTable** tableList, int32 tableListSize); - -/** - * Finds the best index to use in a sort operation. - * - * @param clause An order or group by clause. - */ -void findSortIndex(SQLColumnListClause* clause); - -#endif diff --git a/LitebaseSDK/src/native/parser/SQLDeleteStatement.c b/LitebaseSDK/src/native/parser/SQLDeleteStatement.c deleted file mode 100644 index 7eb0ba7bdb..0000000000 --- a/LitebaseSDK/src/native/parser/SQLDeleteStatement.c +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions to initialize, set, and process a delete statement. - */ - -#include "SQLDeleteStatement.h" - -/** - * Initializes a SQL detete statement for a given SQL. - * - * @param parser The parse structure with parse information concerning the SQL. - * @param isPrepared Indicates if the delete statement is from a prepared statement. - * @return A pointer to a SQLDeleteStatement structure. - */ -SQLDeleteStatement* initSQLDeleteStatement(LitebaseParser* parser, bool isPrepared) -{ - TRACE("initSQLDeleteStatement") - SQLDeleteStatement* deleteStmt = (SQLDeleteStatement*)TC_heapAlloc(parser->heap, sizeof(SQLDeleteStatement)); - SQLBooleanClause* whereClause = deleteStmt->whereClause = parser->whereClause; - Heap heap = deleteStmt->heap = parser->heap; - deleteStmt->type = CMD_DELETE; - - if (isPrepared) // It is only necessary to re-allocate the parser structures if the statement is from a prepared statement. - { - deleteStmt->rsTable = initSQLResultSetTable((*parser->tableList)->tableName, (*parser->tableList)->aliasTableName, heap); - if (whereClause) - { - whereClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, whereClause->fieldsCount * TSIZE); - xmemmove(whereClause->fieldList, parser->whereFieldList, whereClause->fieldsCount * TSIZE); - whereClause->paramList = (SQLBooleanClauseTree**)TC_heapAlloc(heap, whereClause->paramCount * TSIZE); - xmemmove(whereClause->paramList, parser->whereParamList, whereClause->paramCount * TSIZE); - } - } - else - { - deleteStmt->rsTable = *parser->tableList; - if (whereClause) - { - whereClause->fieldList = parser->whereFieldList; - whereClause->paramList = parser->whereParamList; - } - } - - return deleteStmt; -} - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param deleteStmt A SQL delete statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows IllegalStateException If the parameter index is invalid. - */ -bool setNumericParamValueDel(Context context, SQLDeleteStatement* deleteStmt, int32 index, VoidP value, int32 type) -{ - TRACE("setNumericParamValueDel") - - // Checks if the index is within the range. - SQLBooleanClause* whereClause = deleteStmt->whereClause; - if (index < 0 || !whereClause || index >= whereClause->paramCount) - { - TC_throwExceptionNamed(context, "java.lang.IllegalStateException", getMessage(ERR_INVALID_PARAMETER_INDEX), index); - return false; - } - else - return setNumericParamValue(context, whereClause->paramList[index], value, type); -} - -/* - * Sets the value of a string parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param deleteStmt A SQL delete statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @thows IllegalStateException If the parameter index is invalid. - * @return false if an error occurs; true, otherwise. - */ -bool setParamValueStringDel(Context context, SQLDeleteStatement* deleteStmt, int32 index, JCharP value, int32 length) -{ - TRACE("setParamValueStringDel") - - // Checks if the index is within the range. - SQLBooleanClause* whereClause = deleteStmt->whereClause; - if (index < 0 || !whereClause || index >= whereClause->paramCount) - { - TC_throwExceptionNamed(context, "java.lang.IllegalStateException", getMessage(ERR_INVALID_PARAMETER_INDEX), index); - return false; - } - else if (!value) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_PARAM_NULL)); - return false; - } - else - return setParamValueString(context, whereClause->paramList[index], value, length); -} - -/** - * Clears all parameter values of a prepared statement delete. - * - * @param deleteStmt A SQL delete statement. - */ -void clearParamValuesDel(SQLDeleteStatement* deleteStmt) -{ - TRACE("clearParamValuesDel") - if (deleteStmt->whereClause) - { - int32 i = deleteStmt->whereClause->paramCount; - SQLBooleanClauseTree** paramList = deleteStmt->whereClause->paramList; - while (--i >= 0) - paramList[i]->isParamValueDefined = false; - } -} - -/** - * Checks if all parameters values are defined. - * - * @param deleteStmt A SQL delete statement. - * @return true, if all parameters values are defined; false otherwise. - */ -bool allParamValuesDefinedDel(SQLDeleteStatement* deleteStmt) -{ - TRACE("allParamValuesDefinedDel") - if (deleteStmt->whereClause) - { - int32 i = deleteStmt->whereClause->paramCount; - SQLBooleanClauseTree** paramList = deleteStmt->whereClause->paramList; - while (--i >= 0) - if (!paramList[i]->isParamValueDefined) - return false; - } - return true; -} - -/** - * Executes a delete statement. - * - * @param context The thread context where the function is being executed. - * @param deleteStmt A SQL delete statement. - * @return The number of rows deleted. - * @throws DriverException If the record can't be removed from the indices. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -int32 litebaseDoDelete(Context context, SQLDeleteStatement* deleteStmt) -{ - TRACE("litebaseDoDelete") - Table* table = deleteStmt->rsTable->table; - SQLBooleanClause* whereClause = deleteStmt->whereClause; - Index** columnIndexes; - ComposedIndex* compIndex; - ComposedIndex** composedIndexes; - PlainDB* plainDB; - XFile* dbFile; - uint8* basbuf; - Index* index; - int32 nn=0, - i, - id, - columnCount, - numberComposedIndexes; - bool hasIndexes; - Heap heap = null; - - if (!table) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_READ), deleteStmt->rsTable->tableName); - return -1; - } - - dbFile = &(plainDB = &table->db)->db; - basbuf = plainDB->basbuf; - - // If there are indices, this is needed to remove the values from them. - numberComposedIndexes = table->numberComposedIndexes; - columnIndexes = table->columnIndexes; - hasIndexes = (composedIndexes = table->composedIndexes) != null; // juliana@201_6 - i = columnCount = table->columnCount; - while (--i >= 0) - if (columnIndexes[i]) - { - hasIndexes = true; - break; - } - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - if (!setModified(context, table)) - return -1; - - if (!whereClause) // Deletes the whole table. - { - if (hasIndexes) // If the whole table is being deleted, just empties all indexes. - { - i = columnCount; - while (--i >= 0) - if (columnIndexes[i] && !indexDeleteAllRows(context, columnIndexes[i])) - return -1; - - if ((i = numberComposedIndexes)) // juliana@201_6: It now deletes the erases the composed index when deleting the whole table. - while (--i >= 0) - if (!indexDeleteAllRows(context, composedIndexes[i]->index)) - return -1; - } - - // juliana@227_10: Corrected a bug of a delete with no where clause not taking the already deleted rows into consideration when returning - // the number of deleted rows. - nn = plainDB->rowCount - table->deletedRowsCount; - i = table->deletedRowsCount = plainDB->rowCount; - - while (--i >= 0) - { - // Logically deletes the record: changes the attribute to 'deleted'. - if (!plainRead(context, plainDB, i)) - return -1; - xmove4(&id, basbuf); - id = (id & ROW_ID_MASK) | ROW_ATTR_DELETED; - xmove4(basbuf, &id); - if (!plainRewrite(context, plainDB, i)) - return -1; - } - } - else - { - ResultSet* rs; - Key tempKey; - SQLValue** keys; - SQLValue tempKeys[MAXIMUMS + 1]; - uint16* columnOffsets = table->columnOffsets; - uint8* nulls = table->columnNulls; - int32* columnSizes = table->columnSizes; - int8* columnTypes = table->columnTypes; - int8* colIdxTypes; - uint8* columns; - int32 maxSize = 0, - maxSize0 = 0; - - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - // juliana@223_14: solved possible memory problems. - // guich@300: now all records are just marked as deleted instead of physical removal. - if (!(rs = createSimpleResultSet(context, table, whereClause, heap))) - return -1; - - rs->pos = -1; - nn = 0; - - if (hasIndexes) // Removes the value of the index from each column of this record. - { - // Calculates the maximum number of columns that a composed index has. - i = numberComposedIndexes; - maxSize = 1; - while (--i >= 0) - maxSize = MAX(maxSize, composedIndexes[i]->numberColumns); - keys = newSQLValues(maxSize, heap); - - // juliana@202_3: Solved a bug that could cause a GPF when using composed indices. - tempKey.keys = tempKeys; - - // Allocates all the necessary structure for updating the indices at once. - i = columnCount; - while (--i >= 0) - { - if ((index = columnIndexes[i])) // The max size of a char column of a simple index. - maxSize0 = MAX(maxSize0, columnSizes[i]); - - // Calculates the maximum char column size for each column index of all the composed indices. - id = numberComposedIndexes; - maxSize = 0; - while (--id >= 0) - if (i < (compIndex = composedIndexes[id])->numberColumns) - maxSize = MAX(maxSize, compIndex->index->colSizes[i]); - - // Allocates the char buffers if necessary. The simple index will be allocated in the first record. - if (i > 0) - { - if (maxSize > 0) - keys[i]->asChars = (JCharP)TC_heapAlloc(heap, (maxSize << 1) + 2); - } - else - { - // Gets the greatest size between the first column of all the composed indices and the max size of all the simple indices. - if ((maxSize = MAX(maxSize, maxSize0)) > 0) - keys[0]->asChars = (JCharP)TC_heapAlloc(heap, (maxSize << 1) + 2); - } - } - - while (getNextRecord(context, rs, heap)) - { - i = columnCount; - - // juliana@227_11: corrected a bug of an exception being thrown when trying to delete a row with a null in column which has an index. - while (--i >= 0) // Simple indexes. - if ((index = columnIndexes[i]) && isBitUnSet(nulls, i)) - { - if (!readValue(context, plainDB, keys[0], columnOffsets[i], columnTypes[i], basbuf, false, false, false, -1, null)) - goto error; - keySet(&tempKey, &keys[0], index, 1); - if (!indexRemoveValue(context, &tempKey, rs->pos)) - goto error; - } - - if ((i = numberComposedIndexes)) // Composed index. - while (--i >= 0) - { - compIndex = composedIndexes[i]; - index = compIndex->index; - id = compIndex->numberColumns; - colIdxTypes = index->types; - columns = compIndex->columns; - while (--id >= 0) - if (!readValue(context, plainDB, keys[id], columnOffsets[columns[id]], colIdxTypes[id], basbuf, false, false, false, -1, null)) - goto error; - keySet(&tempKey, keys, index, index->numberColumns); - if (!indexRemoveValue(context, &tempKey, rs->pos)) - goto error; - - } - - // Logically deletes the record: changes the attribute to 'deleted'. - xmove4(&id, basbuf); - id = (id & ROW_ID_MASK) | ROW_ATTR_DELETED; - xmove4(basbuf, &id); - if (!plainRewrite(context, plainDB, rs->pos)) - goto error; - nn++; // Increments the number of deleted rows. - } - } - else - while (getNextRecord(context, rs, heap)) - { - // Logically deletes the record: changes the attribute to 'deleted'. - xmove4(&id, basbuf); - id = (id & ROW_ID_MASK) | ROW_ATTR_DELETED; - xmove4(basbuf, &id); - if (!plainRewrite(context, plainDB, rs->pos)) - goto error; - nn++; // Increments the number of deleted rows. - } - table->deletedRowsCount += nn; - heapDestroy(heap); - } - -// juliana@270_32: corrected a bug of a delete not updating the number of total deleted rows in the metadata when there is an index corruption. -error: - if (nn > 0 && !tableSaveMetaData(context, table, TSMD_ONLY_DELETEDROWSCOUNT)) - return -1; - - // juliana@227_3: improved table files flush dealing. - // juliana@270_25: corrected a possible lose of records in recover table when 10 is passed to LitebaseConnection.setRowInc(). - if (!dbFile->dontFlush) // juliana@202_23: flushs the files to disk when row increment is the default. - { - if (dbFile->cacheIsDirty && !flushCache(context, dbFile)) // Flushs .db. - return -1; - if (plainDB->dbo.cacheIsDirty && !flushCache(context, &plainDB->dbo)) // Flushs .dbo. - return -1; - } - return nn; - - heapDestroy(heap); - return -1; -} - -/** - * Binds a SQL DELETE expression. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param deleteStmt A SQL delete statement. - * @return true, if the statement was bound successfully; false otherwise. - */ -bool litebaseBindDeleteStatement(Context context, TCObject driver, SQLDeleteStatement* deleteStmt) -{ - TRACE("litebaseBindDeleteStatement") - Table *table = deleteStmt->rsTable->table = getTable(context, driver, deleteStmt->rsTable->tableName); - - // Binds the delete statement to its table. - if (!table || (deleteStmt->whereClause && !bindColumnsSQLBooleanClauseSimple(context, deleteStmt->whereClause, deleteStmt->rsTable, - deleteStmt->heap))) - return false; - - return true; -} diff --git a/LitebaseSDK/src/native/parser/SQLDeleteStatement.h b/LitebaseSDK/src/native/parser/SQLDeleteStatement.h deleted file mode 100644 index 1b44cc5113..0000000000 --- a/LitebaseSDK/src/native/parser/SQLDeleteStatement.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions to initialize, set, and process a delete statement. - */ - -#ifndef LITEBASE_SQLDELETESTATEMENT_H -#define LITEBASE_SQLDELETESTATEMENT_H - -#include "Litebase.h" - -/** - * Initializes a SQL detete statement for a given SQL. - * - * @param parser The parse structure with parse information concerning the SQL. - * @param isPrepared Indicates if the delete statement is from a prepared statement. - * @return A pointer to a SQLDeleteStatement structure. - */ -SQLDeleteStatement* initSQLDeleteStatement(LitebaseParser* parser, bool isPrepared); - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param deleteStmt A SQL delete statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows IllegalStateException If the parameter index is invalid. - */ -bool setNumericParamValueDel(Context context, SQLDeleteStatement* deleteStmt, int32 index, VoidP value, int32 type); - -/* - * Sets the value of a string parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param deleteStmt A SQL delete statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @thows IllegalStateException If the parameter index is invalid. - * @return false if an error occurs; true, otherwise. - */ -bool setParamValueStringDel(Context context, SQLDeleteStatement* deleteStmt, int32 index, JCharP value, int32 length); - -/** - * Clears all parameter values of a prepared statement delete. - * - * @param deleteStmt A SQL delete statement. - */ -void clearParamValuesDel(SQLDeleteStatement* deleteStmt); - -/** - * Checks if all parameters values are defined. - * - * @param deleteStmt A SQL delete statement. - * @return true, if all parameters values are defined; false otherwise. - */ -bool allParamValuesDefinedDel(SQLDeleteStatement* deleteStmt); - -/** - * Executes a delete statement. - * - * @param context The thread context where the function is being executed. - * @param deleteStmt A SQL delete statement. - * @return The number of rows deleted. - * @throws DriverException If the record can't be removed from the indices. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -int32 litebaseDoDelete(Context context, SQLDeleteStatement* deleteStmt); - -/** - * Binds a SQL DELETE expression. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param deleteStmt A SQL delete statement. - * @return true, if the statement was bound successfully; false otherwise. - */ -bool litebaseBindDeleteStatement(Context context, TCObject driver, SQLDeleteStatement* deleteStmt); - -#endif - diff --git a/LitebaseSDK/src/native/parser/SQLInsertStatement.c b/LitebaseSDK/src/native/parser/SQLInsertStatement.c deleted file mode 100644 index b00b127a88..0000000000 --- a/LitebaseSDK/src/native/parser/SQLInsertStatement.c +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions to initialize, set, and process an insert statement. - */ - -#include "SQLInsertStatement.h" - -/** - * Constructs an insert statement given the result of the parsing process. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param parser The result of the parsing process. - * @return A pointer to a SQLInsertStatement structure. - * @throws SQLParseException If there is a field named "rowid". - */ -SQLInsertStatement* initSQLInsertStatement(Context context, TCObject driver, LitebaseParser* parser) -{ - TRACE("initSQLInsertStatement") - Heap heap = parser->heap; - uint32 i = parser->fieldNamesSize + 1; - JCharP value; - SQLValue* column; - SQLValue** record; - - // Creates a new insert statement. - SQLInsertStatement* insertStmt = (SQLInsertStatement*)TC_heapAlloc(heap, sizeof(SQLInsertStatement)); - - // On Litebase, a table has no alias name on insert. This has no sense. So the same name of the table will be used as an alias. The parser must - // be changed to understand the alias table name. - // Gets the statement base table. - Table *table = insertStmt->table = getTable(context, driver, insertStmt->tableName = (*parser->tableList)->tableName); - - insertStmt->type = CMD_INSERT; - insertStmt->heap = heap; - - // If it is not possible to load the table, frees the structures and returns. - if (!table) - return null; - - if (((LitebaseParser*)parser)->fieldNamesSize) // Checks if it is not using the default order. - { - // Gets the fields and stores them. - CharP* fields = insertStmt->fields = (CharP*)TC_heapAlloc(heap, i * TSIZE); - CharP* fieldNames = parser->fieldNames; - - *fields = null; - insertStmt->storeNulls = TC_heapAlloc(heap, NUMBEROFBYTES(table->columnCount)); - - while (--i) - // A field cannot have the same hash code of the rowid. - if (TC_hashCode(fields[i] = fieldNames[i - 1]) == HCROWID) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_ROWID_CANNOT_BE_CHANGED), 0); - return null; - } - } - else // The nulls info does not need to be recreated when all the fields are used in the insert. - xmemset(insertStmt->storeNulls = table->storeNulls, false, NUMBEROFBYTES(table->columnCount)); - - // Allocates the record: number of fields + rowid. - record = insertStmt->record = (SQLValue**)TC_heapAlloc(heap, (i = table->columnCount) * TSIZE); - - // Allocates space for the list of the parameters. Worst case: all fields are parameters. - insertStmt->paramIndexes = (uint8*)TC_heapAlloc(heap, i); - insertStmt->paramDefined = (uint8*)TC_heapAlloc(heap, i); - - // juliana@227_9: corrected a possible crash if the number of columns of the insert were greater than the one of table definition. - if ((i = parser->fieldValuesSize + 1) > table->columnCount) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION), 0); - return null; - } - insertStmt->nFields = i; - - *record = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - while (--i) - { - if ((value = parser->fieldValues[i - 1])) // Only stores values that are not null. - { - column = record[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - column->asChars = value; - column->length = TC_JCharPLen(value); - } - else - setBit(insertStmt->storeNulls, i, true); - } - return insertStmt; -} - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows DriverException If the parameter type is incompatible with the column type. - */ -bool setNumericParamValueIns(Context context, SQLInsertStatement* insertStmt, int32 index, VoidP value, int32 type) -{ - TRACE("setNumericParamValueIns") - int32 i; - SQLValue* record; - - if (checkInsertIndex(context, insertStmt, index)) // Checks if the index is within the range of the parameter count. - { - // Checks if the column type is the same of the value type. - if (insertStmt->table->columnTypes[i = insertStmt->paramIndexes[index]] != type) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES), 0); - return false; - } - - // Sets the values of the parameter in its list. - insertStmt->paramDefined[index] = true; - setBit(insertStmt->storeNulls, i, (record = insertStmt->record[i])->isNull = false); - switch (type) - { - case SHORT_TYPE: - record->asShort = *((int16*)value); - break; - case INT_TYPE: - record->asInt = *((int32*)value); - break; - case LONG_TYPE: - record->asLong = *((int64*)value); - break; - case FLOAT_TYPE: - record->asFloat = (float)*((double*)value); - break; - case DOUBLE_TYPE: - record->asDouble = *((double*)value); - } - return true; - } - return false; -} - -/* - * Sets the value of a string or blob parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string or blob. - * @param isStr Indicates if the parameter is a string or a blob. - * @thows DriverException If the parameter type is incompatible with the column type. - * @return false if an error occurs; true, otherwise. - */ -bool setStrBlobParamValueIns(Context context, SQLInsertStatement* insertStmt, int32 index, VoidP value, int32 length, bool isStr) -{ - TRACE("setStrBlobParamValueIns") - int32 i; - SQLValue* record; - - if (checkInsertIndex(context, insertStmt, index)) // Checks if the index is within the range of the parameter count. - { - // If the column is a blob, the value type must be a blob. - if ((!isStr && insertStmt->table->columnTypes[i = insertStmt->paramIndexes[index]] != BLOB_TYPE) - || (isStr && insertStmt->table->columnTypes[i = insertStmt->paramIndexes[index]] == BLOB_TYPE)) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES), 0); - return false; - } - - record = insertStmt->record[i]; - - // Sets the values of the parameter in its list. - insertStmt->paramDefined[index] = true; - if (value) // The value is not null. - { - if (isStr) - record->asChars = value; - else - record->asBlob = value; - record->length = length; - setBit(insertStmt->storeNulls, i, record->isNull = false); - } - else // The value is null. - setBit(insertStmt->storeNulls, i, record->isNull = true); - - return true; - } - return false; -} - -// juliana@223_3: PreparedStatement.setNull() now works for blobs. -/** - * Sets null in a given field. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @throws DriverException If the parameter index is invalid. - * @return false if an error occurs; true, otherwise. - */ -bool setNullIns(Context context, SQLInsertStatement* insertStmt, int32 index) -{ - TRACE("setNull") - SQLValue* record; - int32 i; - - if (checkInsertIndex(context, insertStmt, index)) // Checks if the index is within the range of the parameter count. - { - // The value is null. - (record = insertStmt->record[i = insertStmt->paramIndexes[index]])->length = 0; - record->asChars = null; - record->asBlob = null; - - // Sets the values of the parameter in its list. - setBit(insertStmt->storeNulls, i, insertStmt->paramDefined[index] = record->isNull = true); - - return true; - } - return false; -} - -/** - * Throws an exception if the index to set a parameter in the insert prepared statement is invalid. - * If the index is correct, it erases or creates the record. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @return false if an error occurs; true, otherwise. - * @throws IllegalArgumentException If the parameter index is invalid. - */ -bool checkInsertIndex(Context context, SQLInsertStatement* insertStmt, int32 index) -{ - int32 i; - - if (index < 0 || index >= insertStmt->paramCount) - { - TC_throwExceptionNamed(context, "java.lang.IllegalArgumentException", getMessage(ERR_INVALID_PARAMETER_INDEX), index); - return false; - } - - if (insertStmt->record[i = insertStmt->paramIndexes[index]]) - xmemzero(insertStmt->record[i], sizeof(SQLValue)); - else - insertStmt->record[i] = (SQLValue*)TC_heapAlloc(insertStmt->heap, sizeof(SQLValue)); - - return true; -} - -/** - * Clears all parameter values of a prepared statement insert. - * - * @param insertStmt A SQL insert statement. - */ -void clearParamValuesIns(SQLInsertStatement* insertStmt) -{ - TRACE("clearParamValuesIns") - int32 i = insertStmt->paramCount, - j; - uint8* paramIndexes = insertStmt->paramIndexes; - uint8* paramDefined = insertStmt->paramDefined; - uint8* storeNulls = insertStmt->storeNulls; - SQLValue** record = insertStmt->record; - - xmemzero(paramDefined, i); - - while (--i >= 0) - { - xmemzero(record[j = paramIndexes[i]], sizeof(SQLValue)); - setBit(storeNulls, j, paramDefined[j] = false); - record[j]->isNull = true; - } -} - -/** - * Executes an insert statement. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * return true if the insertion was performed successfully; false, otherwise. - */ -bool litebaseDoInsert(Context context, SQLInsertStatement* insertStmt) -{ - TRACE("litebaseDoInsert") - Table* table = insertStmt->table; - Heap heap = heapCreate(); // juliana@223_14: solved possible memory problems. - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - if (!table) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_READ), insertStmt->tableName); - goto error; - } - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - // Verifies if the nulls do not violate a null restriction and writes the record. - if (!verifyNullValues(context, table, insertStmt->record, CMD_INSERT, 0) - || !setModified(context, table) - || !writeRecord(context, table, insertStmt->record, -1, heap)) - goto error; - - heapDestroy(heap); - return true; - -error: - heapDestroy(heap); - return false; -} - -/** - * Binds an insert statement. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @return true, if the statement was bound successfully; false otherwise. - * @throws SQLParseException If the number of values inserted is different from the table definition. - */ -bool litebaseBindInsertStatement(Context context, SQLInsertStatement* insertStmt) -{ - TRACE("litebaseBindInsertStatement") - int32 i = 0, - valuesCount = insertStmt->nFields, - paramCount = 0; - Table* table = insertStmt->table; // Gets the statement base table. - uint8* storeNulls = insertStmt->storeNulls; - SQLValue** record = insertStmt->record; - CharP* fields = insertStmt->fields; - uint8* paramIndexes = insertStmt->paramIndexes; - - while (++i < valuesCount) // Checks if there are undefined values. - // Identifies the values that are placeholders for parameters. - if (record[i] && record[i]->asChars && record[i]->asChars[0] == (JChar)'?' && !record[i]->asChars[1]) - paramIndexes[paramCount++] = i; - - // No fields: The values are ordered. - if (fields && !reorder(context, table, fields, record, storeNulls, &insertStmt->nFields, paramIndexes)) - return false; - - if (insertStmt->nFields != table->columnCount) // The record to be inserted size must math the table record size. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_NUMBER_VALUES_DIFF_TABLE_DEFINITION), insertStmt->nFields - 1); - return false; - } - - if (!convertStringsToValues(context, table, record, insertStmt->nFields)) // Converts the string values to their right types. - return false; - - insertStmt->paramCount = paramCount; - insertStmt->record = record; - return true; -} diff --git a/LitebaseSDK/src/native/parser/SQLInsertStatement.h b/LitebaseSDK/src/native/parser/SQLInsertStatement.h deleted file mode 100644 index 0fb5c85656..0000000000 --- a/LitebaseSDK/src/native/parser/SQLInsertStatement.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions to initialize, set, and process an insert statement. - */ - -#ifndef LITEBASE_SQLINSERTSTATEMENT_H -#define LITEBASE_SQLINSERTSTATEMENT_H - -#include "Litebase.h" - -/** - * Constructs an insert statement given the result of the parsing process. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param parser The result of the parsing process. - * @return A pointer to a SQLInsertStatement structure. - * @throws SQLParseException If there is a field named "rowid". - */ -SQLInsertStatement* initSQLInsertStatement(Context context, TCObject driver, LitebaseParser* parser); - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows DriverException If the parameter type is incompatible with the column type. - */ -bool setNumericParamValueIns(Context context, SQLInsertStatement* insertStmt, int32 index, VoidP value, int32 type); - -/* - * Sets the value of a string or blob parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string or blob. - * @param isStr Indicates if the parameter is a string or a blob. - * @thows DriverException If the parameter type is incompatible with the column type. - * @return false if an error occurs; true, otherwise. - */ -bool setStrBlobParamValueIns(Context context, SQLInsertStatement* insertStmt, int32 index, VoidP value, int32 len, bool isStr); - -// juliana@223_3: PreparedStatement.setNull() now works for blobs. -/** - * Sets null in a given field. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @throws DriverException If the parameter index is invalid. - * @return false if an error occurs; true, otherwise. - */ -bool setNullIns(Context context, SQLInsertStatement* insertStmt, int32 index); - -/** - * Throws an exception if the index to set a parameter in the insert prepared statement is invalid. - * If the index is correct, it erases or creates the record. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @param index The index of the parameter. - * @return false if an error occurs; true, otherwise. - * @throws IllegalArgumentException If the parameter index is invalid. - */ -bool checkInsertIndex(Context context, SQLInsertStatement* insertStmt, int32 index); - -/** - * Clears all parameter values of a prepared statement insert. - * - * @param insertStmt A SQL insert statement. - */ -void clearParamValuesIns(SQLInsertStatement* insertStmt); - -/** - * Executes an insert statement. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * return true if the insertion was performed successfully; false, otherwise. - */ -bool litebaseDoInsert(Context context, SQLInsertStatement* insertStmt); - -/** - * Binds an insert statement. - * - * @param context The thread context where the function is being executed. - * @param insertStmt A SQL insert statement. - * @return true, if the statement was bound successfully; false otherwise. - * @throws SQLParseException If the number of values inserted is different from the table definition. - */ -bool litebaseBindInsertStatement(Context context, SQLInsertStatement* insertStmt); - -#endif diff --git a/LitebaseSDK/src/native/parser/SQLSelectStatement.c b/LitebaseSDK/src/native/parser/SQLSelectStatement.c deleted file mode 100644 index e636ec2ec0..0000000000 --- a/LitebaseSDK/src/native/parser/SQLSelectStatement.c +++ /dev/null @@ -1,2920 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions to initialize, set, and process a select statement. - */ - -#include "SQLSelectStatement.h" - -/** - * Creates and initializes a SQL select statement. - * - * @param parser The structure returned from the parsing process. - * @param isPrepared Indicates if the delete statement is from a prepared statement. - * @return A pointer to a SQLSelectStatement structure. - */ -SQLSelectStatement* initSQLSelectStatement(LitebaseParser* parser, bool isPrepared) -{ - TRACE("initSQLSelectStatement") - Heap heap = parser->heap; - SQLSelectStatement* selectStmt = (SQLSelectStatement*)TC_heapAlloc(heap, sizeof(SQLSelectStatement)); - SQLBooleanClause* whereClause = selectStmt->whereClause = parser->whereClause; // Sets the where clause. - SQLBooleanClause* havingClause = selectStmt->havingClause = parser->havingClause; // Sets the having clause. - SQLColumnListClause* listClause; - SQLSelectClause* selectClause; - int32 count; - - selectStmt->type = CMD_SELECT; // Sets the type of statement. - parser->select.heap = heap; - - // Sets the select clause, its field list, and its hash table. - parser->select.htName2index = TC_htNew(MAXIMUMS << 1, heap); - selectClause = selectStmt->selectClause = (SQLSelectClause*)TC_heapAlloc(heap, sizeof(SQLSelectClause)); - xmemmove(selectClause, &parser->select, sizeof(SQLSelectClause)); - selectClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, count = ((selectClause->fieldsCount? selectClause->fieldsCount : MAXIMUMS) * TSIZE)); - xmemmove(selectClause->fieldList, parser->selectFieldList, count); - selectClause->tableList = (SQLResultSetTable**)TC_heapAlloc(heap, count = (parser->select.tableListSize * TSIZE)); - xmemmove(selectClause->tableList, parser->tableList, count); - - if (isPrepared) // It is only necessary to re-allocate the parser structures if the statement is from a prepared statement. - { - if (parser->groupBy.fieldsCount) // Sets the group by clause. - { - listClause = selectStmt->groupByClause = (SQLColumnListClause*)TC_heapAlloc(heap, sizeof(SQLColumnListClause)); - xmemmove(listClause, &parser->groupBy, sizeof(SQLColumnListClause)); - listClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, count = (parser->groupBy.fieldsCount * TSIZE)); - xmemmove(listClause->fieldList, parser->groupByfieldList, count); - } - - if (parser->orderBy.fieldsCount) // Sets the order by clause. - { - listClause = selectStmt->orderByClause = (SQLColumnListClause*)TC_heapAlloc(heap, sizeof(SQLColumnListClause)); - xmemmove(listClause, &parser->orderBy, sizeof(SQLColumnListClause)); - listClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, count = (parser->orderBy.fieldsCount * TSIZE)); - xmemmove(listClause->fieldList, parser->orderByfieldList, count); - } - - if (whereClause) - { - whereClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, count = (whereClause->fieldsCount * TSIZE)); - xmemmove(whereClause->fieldList, parser->whereFieldList, count); - whereClause->paramList = (SQLBooleanClauseTree**)TC_heapAlloc(heap, count = (whereClause->paramCount * TSIZE)); - xmemmove(whereClause->paramList, parser->whereParamList, count); - } - - if (havingClause) - { - havingClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, count = (havingClause->fieldsCount * TSIZE)); - xmemmove(havingClause->fieldList, parser->havingFieldList, count); - havingClause->paramList = (SQLBooleanClauseTree**)TC_heapAlloc(heap, count = (havingClause->paramCount * TSIZE)); - xmemmove(havingClause->paramList, parser->havingParamList, count); - } - } - else - { - if (parser->groupBy.fieldsCount) // Sets the group by clause. - { - selectStmt->groupByClause = &parser->groupBy; - selectStmt->groupByClause->fieldList = parser->groupByfieldList; - } - if (parser->orderBy.fieldsCount) // Sets the order by clause. - { - selectStmt->orderByClause = &parser->orderBy; - selectStmt->orderByClause->fieldList = parser->orderByfieldList; - } - - if (whereClause) - { - whereClause->fieldList = parser->whereFieldList; - whereClause->paramList = parser->whereParamList; - } - if (havingClause) - { - havingClause->fieldList = parser->havingFieldList; - havingClause->paramList = parser->havingParamList; - } - } - return selectStmt; -} - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param selectStmt A SQL select statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows DriverException If the parameter index is invalid. - */ -bool setNumericParamValueSel(Context context, SQLSelectStatement* selectStmt, int32 index, VoidP value, int32 type) -{ - TRACE("setNumericParamValueSel") - SQLBooleanClause* whereClause = selectStmt->whereClause; - SQLBooleanClause* havingClause = selectStmt->havingClause; - - // Gets the parameter count and checks if the index is within the range. - int32 whereParamCount = whereClause? whereClause->paramCount : 0; - - if (index < 0 || index >= (whereParamCount + (havingClause? havingClause->paramCount : 0))) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PARAMETER_INDEX), index); - return false; - } - else - if (index < whereParamCount) // Sets the parameter value in its proper place. - return setNumericParamValue(context, whereClause->paramList[index], value, type); - else - return setNumericParamValue(context, havingClause->paramList[index - whereParamCount], value, type); -} - -/* - * Sets the value of a string parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param selectStmt A SQL select statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @thows DriverException If the parameter index is invalid. - * @return false if an error occurs; true, otherwise. - */ -bool setParamValueStringSel(Context context, SQLSelectStatement* selectStmt, int32 index, JCharP value, int32 length) -{ - TRACE("setParamValueStringSel") - SQLBooleanClause* whereClause = selectStmt->whereClause; - SQLBooleanClause* havingClause = selectStmt->havingClause; - - // Gets the parameter count and checks if the index is within the range. - int32 whereParamCount = whereClause? whereClause->paramCount : 0; - - if (index < 0 || index >= (whereParamCount + (havingClause? havingClause->paramCount : 0))) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INVALID_PARAMETER_INDEX), index); - return false; - } - else if (!value) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_PARAM_NULL)); - return false; - } - else if (index < whereParamCount) // Sets the parameter value in its proper place. - return setParamValueString(context, whereClause->paramList[index], value, length); - else - return setParamValueString(context, havingClause->paramList[index - whereParamCount], value, length); -} - -/** - * Clears all parameter values of a prepared statement select. - * - * @param selectStmt A SQL select statement. - */ -void clearParamValuesSel(SQLSelectStatement* selectStmt) -{ - TRACE("clearParamValuesSel") - int32 i; - SQLBooleanClause* clause; - SQLBooleanClauseTree** paramList; - if ((clause = selectStmt->whereClause)) // Clears all the parameters of the where clause. - { - i = clause->paramCount; - paramList = clause->paramList; - while (--i >= 0) - paramList[i]->isParamValueDefined = false; - } - - if ((clause = selectStmt->havingClause)) // Clears all the parameters of the having clause. - { - i = clause->paramCount; - paramList = clause->paramList; - while (--i >= 0) - paramList[i]->isParamValueDefined = false; - } -} - -/** - * Checks if all parameters values are defined. - * - * @param selectStmt A SQL select statement. - * @return true, if all parameters values are defined; false otherwise. - */ -bool allParamValuesDefinedSel(SQLSelectStatement* selectStmt) -{ - TRACE("allParamValuesDefinedSel") - int32 i; - SQLBooleanClause* clause; - SQLBooleanClauseTree** paramList; - if ((clause = selectStmt->whereClause)) // Checks if all the where clause parameters are defined. - { - i = clause->paramCount; - paramList = clause->paramList; - while (--i >= 0) - if (!paramList[i]->isParamValueDefined) - return false; - } - - if ((clause = selectStmt->havingClause)) // Checks if all the having clause parameters are defined. - { - i = clause->paramCount; - paramList = clause->paramList; - while (--i >= 0) - if (!paramList[i]->isParamValueDefined) - return false; - } - - return true; -} - -/** - * Executes a select statement. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param selectStmt A SQL select statement. - * @return A result set returned by the query execution. - * @throws DriverException If the record can't be removed from the indices. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -TCObject litebaseDoSelect(Context context, TCObject driver, SQLSelectStatement* selectStmt) -{ - TRACE("litebaseDoSelect") - ResultSet* bag; - SQLSelectClause* selectClause = selectStmt->selectClause; - SQLResultSetTable** tableList = selectClause->tableList; - Table* rsBaseTable; - bool isSimpleSelect = false; - TCObject resultSet = null; - Heap heap; - - int32 i = selectClause->tableListSize; - while (--i >= 0) - { - if (!tableList[i]->table) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_READ), tableList[i]->tableName); - return null; - } - } - - // juliana@212_4: if the select fields are in the table order beginning with rowid, do not build a temporary table. - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - // juliana@210_1: select * from table_name does not create a temporary table anymore. - if (!selectStmt->groupByClause && !selectStmt->havingClause && !selectStmt->orderByClause && !selectStmt->whereClause - && selectClause->tableListSize == 1 && !selectClause->hasAggFunctions) - { - isSimpleSelect = true; - rsBaseTable = (*tableList)->table; - rsBaseTable->answerCount = -1; - } - else // Generates the result set and stores it in a temporary table if necessary. - { - SQLResultSetField** fieldList = selectClause->fieldList; - int32 fieldListLen = selectClause->fieldsCount; - if (!(rsBaseTable = generateResultSetTable(context, driver, selectStmt))) // Temporary table. - return null; - - if (!*rsBaseTable->name) - { - // Remaps the table column names to use the aliases of the select statement instead of the original column names. - // Releases the unused memory (rowinc is 100 by default for result sets). This must be used only for temporary tables. - if (!remapColumnsNames2Aliases(context, rsBaseTable, fieldList, fieldListLen) - || !plainShrinkToSize(context, &rsBaseTable->db)) // guich@201_9: always shrink the .db and .dbo memory files. - { - freeTable(context, rsBaseTable, false, true); - return null; - } - } - } - - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { -finish: - heapDestroy(heap); - if (!*rsBaseTable->name) // juliana@223_14: solved possible memory problems. - freeTable(context, rsBaseTable, false, true); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - - // Creates the result set without passing any fields or WHERE clause, since all the records and all the fields in the temporary table are part - // of the result set. - bag = createResultSetForSelect(context, rsBaseTable, null, heap); - bag->intHashtable = selectClause->htName2index; - bag->selectClause = selectClause; - bag->isSimpleSelect = isSimpleSelect; // juliana@210_1: select * from table_name does not create a temporary table anymore. - bag->driver = driver; // juliana@227_2 - - // juliana@223_13: corrected a bug that could break the application when freeing a result set of a prepared statement. - bag->isPrepared = selectClause->isPrepared; - - if (rsBaseTable->answerCount >= 0) - { - bag->answerCount = rsBaseTable->answerCount; - - // juliana@263_3: corrected a bug where a new result set data could overlap an older result set data if both were related to the same table. - if (!(bag->allRowsBitmap = (uint8*)xmalloc(rsBaseTable->allRowsBitmapLength))) - goto finish; - xmemmove(bag->allRowsBitmap, rsBaseTable->allRowsBitmap, rsBaseTable->allRowsBitmapLength); - } - - if ((resultSet = TC_createObject(context, "litebase.ResultSet"))) - { - setResultSetBag(resultSet, bag); - return resultSet; - } - - freeResultSet(bag); - return null; -} - -/** - * Binds a select statement. - * - * @param context The thread context where the function is being executed. - * @param driver The Litebase connection. - * @param selectStmt A SQL select statement. - * @return true, if the statement was bound successfully; false otherwise. - */ -bool litebaseBindSelectStatement(Context context, TCObject driver, SQLSelectStatement* selectStmt) -{ - TRACE("litebaseBindSelectStatement") - SQLResultSetTable** tableList = selectStmt->selectClause->tableList; - int32 i = selectStmt->selectClause->tableListSize; - Table *table; - - while (--i >= 0) // Gets all tables from the select statement. - { - if (!(table = tableList[i]->table = getTable(context, driver, tableList[i]->tableName))) - return false; - } - - if (!bindSelectStatement(context, selectStmt)) // Validates and binds the select statement to the table list. - return false; - orderTablesToJoin(selectStmt); // Finds the best table order for the join operation. - if (!validateSelectStatement(context, selectStmt)) - return false; // Validates the select statement. - return true; -} - -/** - * Tries to put as inner table a table that has an index used more often in the where clause, when the where clause has a comparison between - * fields from different tables. e.g.: select * from table1, table2 where table1.field1 = table2.field2 If only - * table1.field1 has index, changes the select to: select * from table2, table1 where table1.field1 = table2.field2. - * If both tables has the same level of index using, sorts them by the row count. - * - * @param selectStmt A SQL select statement. - */ -void orderTablesToJoin(SQLSelectStatement* selectStmt) -{ - TRACE("orderTablesToJoin") - SQLSelectClause* selectClause = selectStmt->selectClause; - SQLResultSetTable** tableList = selectClause->tableList; - SQLBooleanClause* whereClause = selectStmt->whereClause; - SQLResultSetTable* rsTableAux1; - SQLResultSetTable* rsTableAux2; - Table* tableAux1; - Table* tableAux2; - SQLResultSetField** fieldList = whereClause? whereClause->fieldList : null; - int32 size = selectClause->tableListSize, - i = size, - j, - k, - highest; - uint8 startedIndex[MAXIMUMS], - changedTo[MAXIMUMS]; - - if (size == 1) // size == 1 is not a join. - return; - - // Starts the weight of the where clause expression tree. - while (--i >= 0) - { - tableList[i]->table->weight = 0; - startedIndex[i] = changedTo[i] = i; - } - if (whereClause) - weightTheTree(whereClause->expressionTree); - - i = size; - while (--i >= 0) // Reorders the tables according to the weight. - { - highest = -1; - tableAux1 = (rsTableAux1 = tableList[j = i])->table; - while (--j >= 0) - // juliana@238_2: improved join table reordering. - // Takes the table size into consideration. - if (tableAux1->weight > (tableAux2 = (rsTableAux2 = tableList[j])->table)->weight - || (tableAux1->weight == tableAux2->weight && tableAux1->db.rowCount > tableAux2->db.rowCount)) - { - rsTableAux1 = rsTableAux2; - highest = j; - } - - if (highest != -1) // Changes table order. - { - tableList[highest] = tableList[i]; - tableList[i] = rsTableAux1; - changedTo[startedIndex[highest]] = i; - changedTo[k = startedIndex[i]] = highest; - startedIndex[i] = startedIndex[highest]; - startedIndex[highest] = k; - } - } - if (whereClause) // Rearranges the indexRs of the where clause field list. - { - i = whereClause->fieldsCount; - while (--i >= 0) - fieldList[i]->indexRs = changedTo[fieldList[i]->indexRs]; - } -} - -/** - * Binds the SQLSelectStatement to the select clause tables. - * - * @param context The thread context where the function is being executed. - * @param selectStmt A SQL select statement. - * @return true if the statement could be corrected bound; false, otherwise. - */ -bool bindSelectStatement(Context context, SQLSelectStatement* selectStmt) -{ - TRACE("bindSelectStatement") - - // First thing to do is to bind the columns in all clauses. - int8* columnTypes; - SQLSelectClause* selectClause = selectStmt->selectClause; - Hashtable* names2Index = &selectClause->htName2index; - SQLResultSetTable** tableList = selectClause->tableList; - int32 count = 0, - size = selectClause->tableListSize, - i = size; - Table* table; - - while (--i >= 0) - count += tableList[i]->table->columnCount; - columnTypes = (int8*)TC_heapAlloc(selectClause->heap, count); - count = 0; - i = size; - while (--i >= 0) // Adds the column types properties of all tables to a big array. - { - table = tableList[i]->table; - xmemmove(&columnTypes[count], table->columnTypes, table->columnCount); - count += table->columnCount; - } - - // Binds the SQL Clauses. Note: The HAVING clause will have a late binding. - // Binds the select clause. - // Binds the where clause if it exists. - // Binds the group by clause if it exists. - // Binds the order by clause if it exists. - if (!bindColumnsSQLSelectClause(context, selectClause) - || (selectStmt->whereClause - && !bindColumnsSQLBooleanClause(context, selectStmt->whereClause, names2Index, columnTypes, tableList, size, selectStmt->selectClause->heap)) - || (selectStmt->groupByClause && !bindColumnsSQLColumnListClause(context, selectStmt->groupByClause, names2Index, columnTypes, tableList, size)) - || (selectStmt->orderByClause && !bindColumnsSQLColumnListClause(context, selectStmt->orderByClause, names2Index, columnTypes, tableList, size))) - return false; - return true; -} - -/** - * Validates the SQLSelectStatement. - * - * @param context The thread context where the function is being executed. - * @param selectStmt The select statement to be validated. - * @return false if a SQLParseException occurs; true, otherwise. - * @throws SQLParseException If the order by and group by clauses do not match, if a query with group by is not well-formed, if there is a - * having clause without an aggregation, a field in the having clause is not in the select clause, there is no order by and there are aggregated - * functions mixed with real columns, or there is an aggregation with an order by clause and no group by clause. - */ -bool validateSelectStatement(Context context, SQLSelectStatement* selectStmt) -{ - TRACE("validateSelectStatement") - SQLSelectClause* selectClause = selectStmt->selectClause; - int32 selectCount = selectClause->fieldsCount, - i = selectCount; - SQLColumnListClause* groupByClause = selectStmt->groupByClause; - SQLColumnListClause* orderByClause = selectStmt->orderByClause; - SQLBooleanClause* havingClause = selectStmt->havingClause; - SQLResultSetField** selectFields = selectClause->fieldList; - SQLResultSetField* field1; - - // Checks if the order by and group by clauses match. - if (groupByClause && orderByClause && !sqlcolumnlistclauseEquals(groupByClause, orderByClause)) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_ORDER_GROUPBY_MUST_MATCH), 0); - return false; - } - - if (groupByClause) // Validates the group by clause if it exists. - { - while (--i >= 0) // Checks if all fields referenced in the HAVING clause are listed as aliased aggregated functions in the SELECT clause. - { - field1 = selectFields[i]; - - // For now, there is no support for queries with GROUP BY and virtual columns in the SELECT clause that are not aggregated functions. - if (!field1->isAggregatedFunction && field1->isVirtual) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_VIRTUAL_COLUMN_ON_GROUPBY), 0); - return false; - } - - // Checks if every non-aggregated function field that is listed in the SELECT clause is listed in the GROUP BY clause. - if (!field1->isAggregatedFunction && !sqlcolumnlistclauseContains(groupByClause, field1->tableColIndex)) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_AGGREG_FUNCTION_ISNOT_ON_SELECT), 0); - return false; - } - } - - if (havingClause) // Checks if all fields referenced in the HAVING clause are listed as aliased aggregated functions in the SELECT clause. - { - int32 havingFieldsCount = havingClause->fieldsCount, - j; - SQLResultSetField** havingFields = havingClause->fieldList; - SQLResultSetField* field2; - bool found = false; - i = havingFieldsCount; - while (--i >= 0) - { - field1 = havingFields[i]; - j = selectCount; - while (--j >= 0) - { - if (field1->aliasHashCode == (field2 = selectFields[j])->aliasHashCode) - { - if (field2->isAggregatedFunction) - { - found = true; - break; - } - - // It is not an aggregated function. Throws an exception. - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_IS_NOT_AGGREG_FUNCTION), field1->alias); - return false; - } - } - - if (!found) // The having clause fields must be in the select clause fields. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_WAS_NOT_LISTED_ON_AGGREG_FUNCTION), field1->alias); - return false; - } - } - } - } - else - { - // If there is no 'GROUP BY' clause, there can not be aggregated functions mixed with real columns in the SELECT clause. - if (selectClause->hasRealColumns && selectClause->hasAggFunctions) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_CANNOT_MIX_AGGREG_FUNCTION), 0); - return false; - } - - // If there are aggregate functions with an ORDER BY clause with no GROUP BY clause, also raises an exception. - if (selectClause->hasAggFunctions && orderByClause) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_CANNOT_HAVE_AGGREG_AND_NO_GROUPBY), 0); - return false; - } - } - return true; -} - -/** - * Generates a table to store the result set. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param selectStmt The select statement to be validated. - * @return The temporary result set table or null if an error occurs. - * @throws OutOfMemoryError If there is not enougth memory allocate memory. - */ -Table* generateResultSetTable(Context context, TCObject driver, SQLSelectStatement* selectStmt) -{ - TRACE("generateResultSetTable") - SQLSelectClause* selectClause = selectStmt->selectClause; - int32 numTables = selectClause->tableListSize, - i, - j, - count = 0, - totalRecords = 0, - size = 0, - selectFieldsCount = selectClause->fieldsCount, - aggFunctionsColsCount = 0, - groupCount, - row = -1, - numberRows, - answerCount, - numOfBytes; - bool aggFunctionExist, - writeDelayed, - isTableTemporary, - countQueryWithWhere = false, - useIndex = true; - SQLResultSetTable** tableList = selectClause->tableList; - SQLBooleanClause* whereClause = selectStmt->whereClause; - SQLColumnListClause* groupByClause = selectStmt->groupByClause; - SQLColumnListClause* orderByClause = selectStmt->orderByClause; - SQLColumnListClause* sortListClause; - SQLBooleanClause* havingClause = selectStmt->havingClause; - SQLResultSetField** fieldList = selectClause->fieldList; - SQLResultSetField** groupList = groupByClause? groupByClause->fieldList : null; - Table* tempTable1 = null; - Table* tempTable2 = null; - Table* tempTable3 = null; - CharP countAlias = null; - int8 columnTypes[MAXIMUMS], - aggFunctionsCodes[MAXIMUMS]; - uint8 nullsCurRecord[NUMBEROFBYTES(MAXIMUMS + 1)]; - int16 columnIndexes[MAXIMUMS]; - int32 columnHashes[MAXIMUMS], - columnSizes[MAXIMUMS], - aggFunctionsRealParamCols[MAXIMUMS], - groupCountCols[MAXIMUMS], - aggFunctionsParamCols[MAXIMUMS]; - size_t columnIndexesTables[MAXIMUMS]; - SQLResultSetField* field; - SQLResultSetField* param; - ResultSet* rsTemp = null; - uint8* nullsPrevRecord; - uint8* allRowsBitmap; - uint8* columnNulls0; - int8* origColumnTypesItems; - int32* columnSizesItems1; - int32* columnSizesItems2; - int32* paramCols = null; - SQLValue* aggFunctionsRunTotals = null; - SQLValue** prevRecord = null; - SQLValue** curRecord; - SQLValue** record1; - SQLValue** record2; - ResultSet* listRsTemp[MAXIMUMS]; //rnovais@200_4 - Hashtable colHashesTable; // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - Heap heap = null, - heap_1 = null, - heap_2 = null, - heap_3 = null; - - if (numTables == 1) // The query is not a join. - (tempTable1 = (*tableList)->table)->answerCount = -1; - - // juliana@226_13: corrected a bug where a query of the form "select year(field) as years from table" could be confunded with - // "select count(*) as years from table". - // Optimization for queries that just wants to count the number of records of a table ("SELECT COUNT(*) FROM TABLE"). - if (selectFieldsCount == 1 && !groupByClause && (*fieldList)->sqlFunction == FUNCTION_AGG_COUNT && (*fieldList)->isAggregatedFunction) - { - countAlias = (*fieldList)->alias; - - if (!whereClause) - { - Table* table; - - totalRecords = 1; - i = numTables; - while (--i >= 0) - { - table = tableList[i]->table; - totalRecords *= (table->db.rowCount - table->deletedRowsCount); - } - return createIntValueTable(context, driver, totalRecords, countAlias); - } - else - countQueryWithWhere = true; - selectClause->type = COUNT_WITH_WHERE; - } - - // Gather metadata for the temporary table that will store the result set. - heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - i = -1; - colHashesTable = TC_htNew(selectFieldsCount, heap); - - while (++i < selectFieldsCount) - { - columnSizes[size] = (field = fieldList[i])->size; - - // Decides which hash code to use as the column name in the temp table and which data type to assign to the temporary table. - if (field->isVirtual) - { - if (field->isAggregatedFunction) - { - // Finds the fields that are parameter for a MAX() and MIN() function that can use an index. - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - if (field->sqlFunction == FUNCTION_AGG_MAX || field->sqlFunction == FUNCTION_AGG_MIN) - findMaxMinIndex(field); - - aggFunctionsParamCols[aggFunctionsColsCount] = i; - aggFunctionsCodes[aggFunctionsColsCount++] = field->sqlFunction; - } - - if ((param = field->parameter)) - { - columnTypes[size] = param->dataType; - columnHashes[size] = field->aliasHashCode; - columnIndexes[size] = param->tableColIndex; - columnIndexesTables[size++] = (size_t)field->table; - - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - TC_htPut32(&colHashesTable, param->aliasHashCode, 1); // juliana@253_1: corrected a bug when sorting if the sort field is in a function. - } - else // Uses the parameter hash and data type. - { - columnTypes[size] = field->dataType; // Uses the field data type. - columnHashes[size] = field->aliasHashCode; // Uses the alias hash code instead. - columnIndexes[size] = -1; // This is just a place holder, since this column does not map to any column in the database. - columnIndexesTables[size++] = 0; - } - } - else // A real column was selected. - { - columnTypes[size] = field->dataType; - columnHashes[size] = field->tableColHashCode; - columnIndexes[size] = field->tableColIndex; - columnIndexesTables[size++] = (size_t)field->table; - - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - TC_htPut32(&colHashesTable, field->aliasHashCode, 0); // juliana@253_1: corrected a bug when sorting if the sort field is in a function. - TC_htPut32(&colHashesTable, field->tableColHashCode, 0); - } - } - - sortListClause = (orderByClause? orderByClause : groupByClause); - - if (sortListClause) - { - findSortIndex(sortListClause); // juliana@230_29: order by and group by now use indices on simple queries. - - // Checks if all columns listed in the order by/group by clause were selected. If not, includes the ones that are missing. - // It must be remembered that, if both are present, group by and order by must match. So, it does not matter which one is picked. - count = sortListClause->fieldsCount; - fieldList = sortListClause->fieldList; - - i = -1; - while (++i < count) - { - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - if (!TC_htGet32Inv(&colHashesTable, (field = fieldList[i])->aliasHashCode)) - continue; - - // The sorting column is missing. Adds it to the temporary table. - columnTypes[size] = field->dataType; - columnSizes[size] = field->size; - columnHashes[size] = field->tableColHashCode; - columnIndexesTables[size] = (size_t)field->table; - columnIndexes[size++] = field->tableColIndex; - } - } - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - // Creates the temporary table to store the records that satisfy the WHERE clause. - // For optimization, the first temporary table will NOT be created, in case there is no WHERE clause and sort clause (either ORDER BY or GROUP BY) - // and the SELECT clause contains aggregated functions. In this case, the calculation of the aggregated functions will be made on the existing - // table. - if (!whereClause && !sortListClause && selectClause->hasAggFunctions && numTables == 1) - { - // In this case, there is no need to create the temporary table. Just points the necessary structures of the original table. - totalRecords = tempTable1->db.rowCount; - - // The index should not be used for MAX() and MIN() if not all the fields are MAX() and MIN() or one of the parametes cannot use an index. - i = -1; - while (++i < selectFieldsCount) - if (!(field = fieldList[i])->isAggregatedFunction || field->index < 0 - || (field->sqlFunction != FUNCTION_AGG_MAX && field->sqlFunction != FUNCTION_AGG_MIN)) - { - useIndex = false; - break; - } - } - else - { - // Creates a result set from the table, using the current WHERE clause. - if (!createListResultSetForSelect(context, tableList, numTables, whereClause, listRsTemp, heap)) - goto error; - - rsTemp = *listRsTemp; - - // The index should not be used for MAX() and MIN() if there is a join, a sort or the indices do not resolve all the query. - if (!sortListClause && (!whereClause || !whereClause->expressionTree) && numTables == 1) - { - // The index should not be used for MAX() and MIN() if not all the fields are MAX() and MIN() or one of the parametes cannot use an index. - i = -1; - while (++i < selectFieldsCount) - if (!(field = fieldList[i])->isAggregatedFunction || field->index < 0 - || (field->sqlFunction != FUNCTION_AGG_MAX && field->sqlFunction != FUNCTION_AGG_MIN)) - { - useIndex = false; - break; - } - } - else - useIndex = false; - - // juliana@230_29: order by and group by now use indices on simple queries. - // Only uses index when sorting if all the indices are applied. - if (sortListClause && ((whereClause && whereClause->expressionTree) || selectClause->hasAggFunctions || numTables != 1)) - sortListClause->index = -1; - - // juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. - if ((sortListClause && sortListClause->index == -1) || countQueryWithWhere || numTables != 1) - { - // Optimization for queries of type "SELECT COUNT(*) FROM TABLE WHERE..." Just counts the records of the result set and write it to a table. - if (countQueryWithWhere && numTables == 1) - { - if (!sqlBooleanClausePreVerify(context, whereClause)) - goto error; - rsTemp->pos = -1; - while (getNextRecord(context, rsTemp, heap)) - totalRecords++; - - heapDestroy(heap); - return createIntValueTable(context, driver, totalRecords, countAlias); - } - - heap_1 = heapCreate(); - IF_HEAP_ERROR(heap_1) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - heap_1->greedyAlloc = true; - - // Creates a temporary table to store the result set records and writes the result set records to the temporary table.. - if ((tempTable1 = driverCreateTable(context, driver, null, null, duplicateIntArray(columnHashes, size, heap_1), - duplicateByteArray(columnTypes, size, heap_1), duplicateIntArray(columnSizes, size, heap_1), - null, null, NO_PRIMARY_KEY, NO_PRIMARY_KEY, null, 0, size, heap_1))) - { - IF_HEAP_ERROR(heap_1) // juliana@223_14: solved possible memory problems. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - totalRecords = writeResultSetToTable(context, listRsTemp, numTables, tempTable1, columnIndexes, selectClause, columnIndexesTables, - whereClause? whereClause->type : -1, heap); - } - else // guich@570_97 - goto error; // juliana@223_14: solved possible memory problems. - - if (totalRecords <= 0) // No records retrieved. Exit. - { - if (totalRecords < 0) - goto error; - heapDestroy(heap); - return tempTable1; - } - if (selectClause->type == COUNT_WITH_WHERE) - { - heapDestroy(heap); - freeTable(context, tempTable1, 0, false); - return createIntValueTable(context, driver, totalRecords, countAlias); - } - } - else if (!useIndex && !sortListClause) // A query that use index for MAX() and MIN() should not check now which rows are answered. - { - uint8* allRowsBitmap = tempTable1->allRowsBitmap; - int32 newLength = (tempTable1->db.rowCount + 7) >> 3, - oldLength = allRowsBitmap? tempTable1->allRowsBitmapLength : -1; - - if (newLength > oldLength) - if (!(tempTable1->allRowsBitmap = allRowsBitmap = xrealloc(allRowsBitmap, tempTable1->allRowsBitmapLength = newLength))) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - xmemzero(allRowsBitmap, newLength); - computeAnswer(context, rsTemp, heap); - - if (!selectClause->hasAggFunctions) // Nothing more to be done. Just returns the result. - { - heapDestroy(heap); - return tempTable1; - } - - totalRecords = tempTable1->answerCount; - } - } - - // juliana@230_29: order by and group by now use indices on simple queries. - if (sortListClause) // Sorts the temporary table, if required. - { - if (sortListClause->index == -1) - { - if (!sortTable(context, tempTable1, groupByClause, orderByClause)) - goto error; - } - else - { - heap_1 = heapCreate(); - IF_HEAP_ERROR(heap_1) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - heap_1->greedyAlloc = true; - - // Creates a temporary table to store the result set records and writes the result set records to the temporary table.. - if ((tempTable1 = driverCreateTable(context, driver, null, null, duplicateIntArray(columnHashes, size, heap_1), - duplicateByteArray(columnTypes, size, heap_1), duplicateIntArray(columnSizes, size, heap_1), - null, null, NO_PRIMARY_KEY, NO_PRIMARY_KEY, null, 0, size, heap_1))) - { - PlainDB* plainDB = &tempTable1->db; - Index* index; - SQLValue** record = newSQLValues(i = tempTable1->columnCount, heap); - Table* rsTable = rsTemp->table; - int8* types = tempTable1->columnTypes; - int32* sizes = tempTable1->columnSizes; - - if (!(plainDB->rowAvail = (rsTemp->rowsBitmap.items? bitCount(rsTemp->rowsBitmap.items, rsTemp->rowsBitmap.length) - : rsTable->db.rowCount - rsTable->deletedRowsCount))) - { - heapDestroy(heap); - return tempTable1; - } - - while (--i >= 0) - if (types[i] == CHARS_TYPE || types[i] == CHARS_NOCASE_TYPE) - record[i]->asChars = (JCharP)TC_heapAlloc(heap, (sizes[i] << 1) + 1); - - IF_HEAP_ERROR(heap_1) // juliana@223_14: solved possible memory problems. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - if (!mfGrowTo(context, &plainDB->db, plainDB->rowAvail++ * plainDB->rowSize)) - goto error; - - if (sortListClause->isComposed) - index = rsTable->composedIndexes[sortListClause->index]->index; - else - index = rsTable->columnIndexes[sortListClause->index]; - if (sortListClause->fieldList[0]->isAscending) - { - if (!sortRecordsAsc(context, index, &rsTemp->rowsBitmap, tempTable1, record, columnIndexes, heap)) - goto error; - } - else if (!sortRecordsDesc(context, index, &rsTemp->rowsBitmap, tempTable1, record, columnIndexes, heap)) - goto error; - if (!(totalRecords = plainDB->rowCount)) - { - heapDestroy(heap); - return tempTable1; - } - if (groupByClause && !bindColumnsSQLColumnListClause(context, groupByClause, &tempTable1->htName2index, types, null, 0)) - goto error; - - } - else // guich@570_97 - goto error; // juliana@223_14: solved possible memory problems. - } - } - - // There is still one new temporary table to be created, if the select clause has aggregate functions or here is a group by clause. - if (!selectClause->hasAggFunctions && !groupByClause) - { - heapDestroy(heap); - return tempTable1; - } - - count = tempTable1->columnCount; - - // When creating the new temporary table, removes the extra fields that were created to perform the sorting. - // juliana@270_24: corrected a possible application crash or exception when using order/group by with join. - if (sortListClause && count != selectFieldsCount) - size = selectFieldsCount; - - // Also updates the types and hashcodes to reflect the types and aliases of the - // final temporary table, since they may still reflect the aggregated functions paramList types and hashcodes. - columnSizesItems1 = columnSizes; - - // First preserves the original types, since they will be needed in the aggregated functions running totals calculation. - origColumnTypesItems = tempTable1->columnTypes; - fieldList = selectClause->fieldList; - i = selectClause->fieldsCount; - - while (--i >= 0) - { - columnTypes[i] = (field = fieldList[i])->dataType; - columnSizesItems1[i] = field->size; - columnHashes[i] = field->aliasHashCode; - } - - heap_2 = heapCreate(); - IF_HEAP_ERROR(heap_2) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - heap_2->greedyAlloc = true; - - // Creates the second temporary table. - if (!(tempTable2 = driverCreateTable(context, driver, null, null, duplicateIntArray(columnHashes, size, heap_2), - duplicateByteArray(columnTypes, size, heap_2), duplicateIntArray(columnSizes, size, heap_2), null, null, - NO_PRIMARY_KEY, NO_PRIMARY_KEY, null, 0, size, heap_2))) - goto error; // juliana@223_14: solved possible memory problems. - - if ((aggFunctionExist = selectClause->hasAggFunctions) && !useIndex) // Initializes the aggregated functions running totals. - { - aggFunctionsRunTotals = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue) * selectFieldsCount); - - // If the temporary table points to a real table, stores also the column indexes of aggregate functions parameter list of the real table. - if (*tempTable1->name) - { - i = aggFunctionsColsCount; - while (--i >= 0) - aggFunctionsRealParamCols[i] = (param = fieldList[aggFunctionsParamCols[i]]->parameter) ? param->tableColIndex : -1; - } - } - - // juliana@227_12: corrected a possible bug with MAX() and MIN() with strings. - curRecord = record1 = newSQLValues(i = (count = tempTable1->columnCount) > selectFieldsCount? count : selectFieldsCount, heap); - - // juliana@230_21: MAX() and MIN() now use indices on simple queries. - if (useIndex) - { - Index* index; - IntVector* rowsBitmap = (rsTemp? &rsTemp->rowsBitmap : null); - - // No rows in the answer. - if (!(tempTable1->db.rowCount - tempTable1->deletedRowsCount) || (whereClause && !bitCount(rowsBitmap->items, rowsBitmap->length))) - { - heapDestroy(heap); - return tempTable2; - } - - // Computes the MAX() and MIN() for all the fields. - i = -1; - curRecord[0]->isNull = true; // No rows yet. - while (++i < selectFieldsCount) - { - if ((field = fieldList[i])->isComposed) - index = tempTable1->composedIndexes[field->index]->index; - else - index = tempTable1->columnIndexes[field->index]; - if (field->sqlFunction == FUNCTION_AGG_MAX) - { - if (!findMaxValue(context, index, curRecord[i], rowsBitmap)) - goto error; - } - else if (!findMinValue(context, index, curRecord[i], rowsBitmap)) - goto error; - } - - if (curRecord[0]->isNull) // No rows found: returns an empty table. - { - heapDestroy(heap); - return tempTable2; - } - xmemzero(tempTable2->columnNulls, NUMBEROFBYTES(selectFieldsCount)); - writeRSRecord(context, tempTable2, curRecord); - heapDestroy(heap); - return tempTable2; - } - - prevRecord = record2 = newSQLValues(i, heap); - nullsPrevRecord = tempTable1->columnNulls; - writeDelayed = aggFunctionExist || groupByClause; - - // Loops through the records of the temporary table, to calculate the agregated values and/or write the group records. - paramCols = (isTableTemporary = !*tempTable1->name)? aggFunctionsParamCols: aggFunctionsRealParamCols; - - xmemzero(groupCountCols, aggFunctionsColsCount << 2); // Each column has a groupCount because of the null values. - - // juliana@230_20: solved a possible crash when using aggregation functions with strings. - // Allocates the total space for the strings at once so that they do not need to be reallocated. - columnSizesItems1 = tempTable1->columnSizes; - columnSizesItems2 = tempTable2->columnSizes; - - i = selectFieldsCount; - while (--i >= 0) - if ((i < count && columnSizesItems2[i] > columnSizesItems1[i]) || columnSizesItems2[i]) - { - record1[i]->asChars = (JCharP)TC_heapAlloc(heap, (columnSizesItems2[i] << 1) + 2); - record2[i]->asChars = (JCharP)TC_heapAlloc(heap, (columnSizesItems2[i] << 1) + 2); - } - - i = count; - while (--i >= 0) - if ((i < selectFieldsCount && columnSizesItems1[i] > columnSizesItems2[i]) || (columnSizesItems1[i] && !record1[i]->asChars)) - { - record1[i]->asChars = (JCharP)TC_heapAlloc(heap, (columnSizesItems1[i] << 1) + 2); - record2[i]->asChars = (JCharP)TC_heapAlloc(heap, (columnSizesItems1[i] << 1) + 2); - } - - // juliana@226_5 - i = aggFunctionsColsCount; - while (--i >= 0) - { - // juliana@227_12: corrected a possible bug with MAX() and MIN() with strings. - if (paramCols[i] >= 0 && columnSizesItems1[paramCols[i]]) - aggFunctionsRunTotals[i].asChars = (JCharP)TC_heapAlloc(heap, (columnSizesItems1[paramCols[i]] << 1) + 2); - } - - if (groupByClause) - count = groupByClause->fieldsCount; - - columnNulls0 = tempTable2->columnNulls; - allRowsBitmap = tempTable1->allRowsBitmap; - numberRows = tempTable1->db.rowCount; - answerCount = tempTable1->answerCount; - - // juliana@253_17: correted a possible crash or wrong result when using aggregation functions without using indices on a table with many columns. - numOfBytes = NUMBEROFBYTES(tempTable2->columnCount); - - for (i = -1, groupCount = 0; ++i < totalRecords; groupCount++) - { - if (answerCount >= 0) - { - while (row++ < numberRows && isBitUnSet(allRowsBitmap, row)); - if (!readRecord(context, tempTable1, curRecord, row, nullsCurRecord, null, 0, true, null, null)) - goto error; - } - else if (!readRecord(context, tempTable1, curRecord, i, nullsCurRecord, null, 0, true, null, null)) // juliana@220_3 juliana@227_20 - goto error; - - // Because it is possible to be pointing to a real table, skips deleted records. - if (!isTableTemporary && !recordNotDeleted(tempTable1->db.basbuf)) - { - groupCount--; - continue; - } - - // In case there is a group by, checks if there was a change in the group record composition. - if (groupByClause && groupCount && compareRecords(prevRecord, curRecord, nullsPrevRecord, nullsCurRecord, count, groupList)) - { - if (aggFunctionExist) // Checks if there are aggregate functions and concludes any aggregated function calculation. - endAggFunctionsCalc(prevRecord, groupCount, aggFunctionsRunTotals, aggFunctionsCodes, aggFunctionsParamCols, paramCols, - aggFunctionsColsCount, origColumnTypesItems, groupCountCols); - - // Flushes the previous record and starts a new group counting, taking the null values for the non-aggregate fields into consideration. - xmemmove(columnNulls0, nullsPrevRecord, numOfBytes); - j = aggFunctionsColsCount; - while (--j >= 0) - { - setBit(columnNulls0, aggFunctionsParamCols[j], !groupCountCols[j]); - groupCountCols[j] = 0; - } - - if (!writeRSRecord(context, tempTable2, prevRecord)) - goto error; - groupCount = 0; - } - - // Checks if there are aggregate functions and performs the calculation of the aggregate functions.. - if (aggFunctionExist) - performAggFunctionsCalc(context, curRecord, nullsCurRecord, aggFunctionsRunTotals, aggFunctionsCodes, paramCols, aggFunctionsColsCount, - origColumnTypesItems, groupCountCols); - - if (!writeDelayed) - { - columnNulls0 = nullsCurRecord; - if (!writeRSRecord(context, tempTable2, curRecord)) - goto error; - } - - prevRecord = curRecord; - xmemmove(nullsPrevRecord, nullsCurRecord, numOfBytes); - curRecord = (curRecord == record1)? record2 : record1; - } - - // juliana@227_12: corrected a possible bug with MAX() and MIN() with strings. - if (writeDelayed && groupCount > 0) // If there was adelayed writing, flushes the last record. - { - if (aggFunctionExist) - // Concludes any aggregated function calculation. - endAggFunctionsCalc(prevRecord, groupCount, aggFunctionsRunTotals, aggFunctionsCodes, aggFunctionsParamCols, paramCols, - aggFunctionsColsCount, origColumnTypesItems, groupCountCols); - - // Writes the last record. - xmemmove(columnNulls0, nullsCurRecord, numOfBytes); // Takes the null values for the non-aggregate fields into consideration. - j = aggFunctionsColsCount; - while (--j >= 0) - { - setBit(columnNulls0, aggFunctionsParamCols[j], !groupCountCols[j]); - groupCountCols[j] = 0; - } - - if (!writeRSRecord(context, tempTable2, prevRecord)) - goto error; - } - - if (!*tempTable1->name) // Drops the first temporary table, if it is really a temporary table. - { - freeTable(context, tempTable1, false, false); - tempTable1 = null; - heap_1 = null; // juliana@223_14: solved possible memory problems. - } - - if (!havingClause) // If there is no having clause, returns the temp table 2. - { - heapDestroy(heap); - return tempTable2; - } - - heap_3 = heapCreate(); - IF_HEAP_ERROR(heap_3) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - // juliana@223_14: solved possible memory problems. - // Creates the last and third temporary table to hold the records that comply to the "HAVING" clause. - if (!(tempTable3 = driverCreateTable(context, driver, null, null, duplicateIntArray(columnHashes, size, heap_3), - duplicateByteArray(columnTypes, size, heap_3), duplicateIntArray(columnSizes, size, heap_3), null, null, - NO_PRIMARY_KEY, NO_PRIMARY_KEY, null, 0, size, heap_3))) - goto error; - - // The HAVING clause only works with aliases. So, remaps the table column names, to use the aliases - // of the select statement instead of the original column names. - if (!remapColumnsNames2Aliases(context, tempTable3, fieldList, selectClause->fieldsCount) || - !bindColumnsSQLBooleanClause(context, havingClause, &tempTable3->htName2index, tempTable3->columnTypes, null, 0, heap_3)) - goto error; - - // Creates a result set from the table, using the HAVING clause. - if (!(rsTemp = createResultSetForSelect(context, tempTable2, havingClause, heap))) - goto error; - - // juliana@223_14: solved possible memory problems. - // Writes the result set to the temporary table 3. - if (writeResultSetToTable(context, &rsTemp, 1, tempTable3, null, selectClause, null, -1, heap) <= 0) // Already frees rsTemp. - { - heap = null; - goto error; - } - else - return tempTable3; - -error: - heapDestroy(heap); - if (tempTable1 && !*tempTable1->name) - freeTable(context, tempTable1, false, false); - else - heapDestroy(heap_1); - if (tempTable2) - freeTable(context, tempTable2, false, false); - else - heapDestroy(heap_2); - if (tempTable3) - freeTable(context, tempTable3, false, false); - else - heapDestroy(heap_3); - return null; -} - -/** - * Generates a table to store the result set. - * - * @param context The thread context where the function is being executed. - * @param tableList The table list of the select. - * @param size The number of tables of the select. - * @param whereClause the where clause of the select. - * @param rsList Receives the temporary result set list. - * @param heap A heap to perform some memory allocations. - * @return falseif an error occurs when appling the indices; true, otherwise. - */ -bool createListResultSetForSelect(Context context, SQLResultSetTable** tableList, int32 size, SQLBooleanClause* whereClause, ResultSet** rsList, Heap heap) -{ - TRACE("createListResultSetForSelect") - Table *table; - ResultSet* resultSet; - int32 i = -1; - bool hasComposedIndex = false; - - xmemzero(rsList, size * TSIZE); - while (++i < size) - { - resultSet = createResultSet((table = tableList[i]->table), whereClause, heap); - resultSet->indexRs = i; // Sets the table index. - rsList[i] = resultSet; - - // It is only necessary to have one table with composed indices. - hasComposedIndex = hasComposedIndex | (table->numberComposedIndexes > 0); - } - if (whereClause) // Tries to apply the table indices to generate a bitmap of the rows to be returned. - { - if (size > 1) // Join. - setIndexRsOnTree((*rsList)->whereClause->expressionTree); - if (!generateIndexedRowsMap(context, rsList, size, hasComposedIndex, heap)) - return false; - } - return true; -} - -/** - * Generates an index bit map for a list of result sets. - * - * @param context The thread context where the function is being executed. - * @param rsList The list of result sets. - * @param size The number of tables of the select. - * @param hasComposedIndex Indicates if the table has a composed index. - * @param heap A heap to allocate temporary structures. - * @return true if the function executed correctly; false, otherwise. - */ -bool generateIndexedRowsMap(Context context, ResultSet** rsList, int32 size, bool hasComposedIndex, Heap heap) -{ - TRACE("generateIndexedRowsMap") - ResultSet* rsBag = *rsList; - Table* table = rsBag->table; - SQLBooleanClause* whereClause = rsBag->whereClause; - ComposedIndex** appliedComposedIndexes; - MarkBits* markBits; - int32 maxSize = 1, - count; - - if (whereClause) - { - if (size > 1) - { - if (!applyTableIndexesJoin(whereClause)) - { - rsList[0]->markBits = markBits = TC_heapAlloc(heap, sizeof(MarkBits)); - markBits->leftKey.keys = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - markBits->rightKey.keys = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - markBits->leftOp = TC_heapAlloc(heap, 1); - markBits->rightOp = TC_heapAlloc(heap, 1); - return true; - } - } - else - if (!applyTableIndexes(whereClause, table->columnIndexes, table->columnCount, hasComposedIndex)) - return true; - - count = whereClause->appliedIndexesCount; - appliedComposedIndexes = whereClause->appliedComposedIndexes; - while (--count >= 0) - if (appliedComposedIndexes[count]) - maxSize = MAX(size, appliedComposedIndexes[count]->numberColumns); - - rsList[0]->markBits = markBits = TC_heapAlloc(heap, sizeof(MarkBits)); - markBits->leftKey.keys = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue) * maxSize); - markBits->rightKey.keys = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue) * maxSize); - markBits->leftOp = TC_heapAlloc(heap, maxSize); - markBits->rightOp = TC_heapAlloc(heap, maxSize); - - if (!computeIndex(context, rsList, size, size > 1, -1, null, -1, -1, heap)) - return false; - - if (!whereClause->expressionTree) - while (--size >= 0) // There is no where clause left, since all rows can be returned using the indexes. - rsList[size]->whereClause = null; - } - return true; -} - -/** - * Finds the rows that satisfy the query clause using the indices. - * - * @param context The thread context where the function is being executed. - * @param rsList The result set list, one for each table. - * @param size The number of tables of the select. - * @param isJoin Indicates that the query has a join. - * @param indexRsOnTheFly The index of the result set or -1 if the query is being indexed on the fly. - * @param value The value to be indexed on the fly. - * @param operator The operand type. Used only to index on the fly. - * @param colIndex The index column. Used only to index on the fly. - * @param heap A heap to allocate temporary structures. - * @return true if the function executed correctly; false, otherwise. - */ -bool computeIndex(Context context, ResultSet** rsList, int32 size, bool isJoin, int32 indexRsOnTheFly, SQLValue* value, int32 operator, - int32 colIndex, Heap heap) -{ - // Gets the list of indexes that were applied to the where clause together with the indexes values to search for and the bool operation to apply. - TRACE("computeIndex") - ResultSet* rsBag; - ResultSet* rsListPointer[MAXIMUMS]; // The resulting indexed row bitmap. - bool onTheFly = (indexRsOnTheFly != -1), - isCI, // has composed index? - isMatch; - Table** appliedIndexTables = null; - Table* table; - Index* index; - MarkBits markBits = *rsList[0]->markBits; - SQLBooleanClause* whereClause = (*rsList)->whereClause; - int32 i, - j, - count = 1, - booleanOp, - recordCount, - col, - op; - uint8* indexedCols = whereClause->appliedIndexesCols; - uint8* relationalOps = whereClause->appliedIndexesRelOps; - SQLBooleanClauseTree** indexedValues = whereClause->appliedIndexesValueTree; - ComposedIndex** appliedComposedIndexes = whereClause->appliedComposedIndexes; - IntVector auxBitmap; - - // Gets the list of indexes that were applied to the where clause, together - // with the indexes values to search for and the boolean operation to apply. - if (onTheFly) - booleanOp = rsList[indexRsOnTheFly]->rowsBitmapBoolOp; - else - { - count = whereClause->appliedIndexesCount; - booleanOp = whereClause->appliedIndexesBooleanOp; - } - - rsBag = onTheFly? rsList[indexRsOnTheFly] : *rsList; - recordCount = rsBag->table->db.rowCount; - if (isJoin) // Puts the result set bag in order with the tables. - { - appliedIndexTables = whereClause->appliedIndexesTables; - - i = count; - while (--i >= 0) - { - j = size; - while (--j >= 0) - if (rsList[j]->table == appliedIndexTables[i]) - { - rsBag = rsListPointer[i] = rsList[j]; - rsListPointer[i]->rowsBitmap = newIntBits(recordCount = rsBag->table->db.rowCount - 1, heap); - break; - } - } - } - else - { - if (onTheFly) - { - ResultSet* resultSet = rsList[indexRsOnTheFly]; - if (!resultSet->auxRowsBitmap.items) - resultSet->auxRowsBitmap = newIntBits(recordCount, heap); - else - xmemzero(resultSet->auxRowsBitmap.items, resultSet->auxRowsBitmap.size << 2); - } - else - (*rsList)->rowsBitmap = newIntBits(recordCount, heap); - } - - xmemzero(&auxBitmap, sizeof(IntVector)); - if (count > 1) - auxBitmap = newIntBits(recordCount, heap); - else - xmemzero(&auxBitmap, sizeof(IntVector)); - - rsBag->indexCount = 0; - table = rsBag->table; - - // Loops through all applied indexes, and records the indexed rows in the bitmap. - i = -1; - while (++i < count) - { - isCI = false; - - // Prepares the index row bitmap. - col = op = -1, - size = 1; - isMatch = false; - - if (isJoin) - { - table = appliedIndexTables[i]; - rsBag = rsListPointer[i]; - } - rsBag->indexCount++; - - if (onTheFly) - { - col = colIndex; - op = operator; - isMatch = op == OP_PAT_MATCH_NOT_LIKE || op == OP_PAT_MATCH_LIKE; - markBits.leftKey.keys[0] = *value; - } - else - { - if ((isCI = (appliedComposedIndexes[i] != null))) - { - j = size = appliedComposedIndexes[i]->numberColumns; - while (--j >= 0) - { - if (!(isMatch = (relationalOps[i + j] == OP_PAT_MATCH_NOT_LIKE || relationalOps[i + j] == OP_PAT_MATCH_LIKE)) - && !getOperandValue(context, indexedValues[i + j], &markBits.leftKey.keys[j])) - return false; - } - } - else - { - col = indexedCols[i]; - if (!(isMatch = (op = relationalOps[i]) == OP_PAT_MATCH_NOT_LIKE || op == OP_PAT_MATCH_LIKE) - && !getOperandValue(context, indexedValues[i], &markBits.leftKey.keys[0])) - return false; - } - } - - index = (isCI)? appliedComposedIndexes[i]->index : table->columnIndexes[col]; - markBitsReset(&markBits, (rsBag->indexCount > 1)? &auxBitmap : (onTheFly? &rsBag->auxRowsBitmap - : &rsBag->rowsBitmap)); // Prepared the index row bitmap. - - j = size; - while (--j >= 0) - { - if (isCI) - { - op = relationalOps[i + j]; - isMatch = op == OP_PAT_MATCH_NOT_LIKE || op == OP_PAT_MATCH_LIKE; - } - - // if the operation is 'like x%', then replaces the operand by the value without the % mask. - if (isMatch) - { - // juliana@230_10: solved a bug that could crash the application when more than one index is applied. - markBits.leftKey.keys[j].asChars = indexedValues[i + j]->strToMatch; - markBits.leftKey.keys[j].length = indexedValues[i + j]->lenToMatch; - } - - // Checks if this is a "between" operation. - else if (booleanOp == OP_BOOLEAN_AND && i < (count - 1) && indexedCols[i + 1] == col && (op == OP_REL_GREATER || op == OP_REL_GREATER_EQUAL) - && (relationalOps[i + j + 1] == OP_REL_LESS || relationalOps[i + j + 1] == OP_REL_LESS_EQUAL)) - { - if (!getOperandValue(context, indexedValues[j + i + 1], &markBits.rightKey.keys[0])) - return false; - markBits.rightKey.record = NO_VALUE; - markBits.rightKey.index = index; - markBits.rightOp[j] = relationalOps[j + i++ + 1]; // The next operation is already processed. - } - else - { - switch (col = op) // When searching for !=, <, <=, we'll use the opposite operation instead. - { - case OP_REL_DIFF : - col = OP_REL_EQUAL; - break; - case OP_REL_LESS : - col = OP_REL_GREATER_EQUAL; - break; - case OP_REL_LESS_EQUAL : - col = OP_REL_GREATER; - break; - case OP_PAT_MATCH_NOT_LIKE : - col = OP_PAT_MATCH_LIKE; - } - if (op != col) // All the rows will be marked and only resets the rows that satisfy the opposite operation. - { - xmemset(markBits.indexBitmap->items, 0xFF, markBits.indexBitmap->size << 2); - markBits.bitValue = false; - op = col; - } - } - markBits.leftOp[j] = op; - } - markBits.leftKey.index = index; - markBits.leftKey.record = NO_VALUE; - - switch (op) // Finally, marks all rows that match this value / range of values. - { - case OP_REL_EQUAL: - if (!indexGetValue(context, &markBits.leftKey, &markBits)) - return false; - break; - case OP_REL_GREATER: - case OP_REL_GREATER_EQUAL: - case OP_PAT_MATCH_LIKE: - if (!indexGetGreaterOrEqual(context, &markBits.leftKey, &markBits)) - return false; - } - - // If it is the first index, assigns the index bitmap to the resulting bitmap. Otherwise, merges the index - // bitmap with the existing bitmap, using the boolean operator. - if (rsBag->indexCount > 1) - mergeBitmaps((onTheFly? &rsBag->auxRowsBitmap : &rsBag->rowsBitmap), markBits.indexBitmap, booleanOp); - - rsBag->rowsBitmapBoolOp = booleanOp; // juliana@230_15: corrected a bug when using joins with indices which could cause an OutOfMemoryExcepion. - if (isCI) - i += (size -1); - } - - return true; -} - -/** - * Merges two bitmaps into the first bitmap using the given boolean operator. - * - * @param bitmap1 The first bitmap. - * @param bitmap2 The second bitmap. - * @param booleanOp The boolean operator to be applied. - */ -void mergeBitmaps(IntVector* bitmap1, IntVector* bitmap2, int32 booleanOp) -{ - TRACE("mergeBitmaps") - int32* items1 = bitmap1->items; - int32* items2 = bitmap2->items; - int32 size = bitmap1->size; - - if (booleanOp == OP_BOOLEAN_AND) - while (--size >= 0) - *items1++ &= *items2++; - else - while (--size >= 0) - *items1++ |= *items2++; -} - -/** - * Concludes the calculation of the given aggregated function running totals based on the given record and the group count. - * - * @param record The record that is the parameter for the aggregated function. - * @param groupCount The result of a COUNT(*). - * @param aggFunctionsRunTotals The results of the aggregated functions. - * @param aggFunctionsCodes The aggregated function codes. - * @param aggFunctionsParamCols The columns that are parameters to the aggregated functions. - * @param aggFunctionsRealParamCols The real columns that are parameters to the aggregated functions. - * @param aggFunctionsColsCount The number of columns that are parameters to the aggregated functions. - * @param columnTypes The types of the columns. - * @param groupCountCols The count for the groups. - */ -void endAggFunctionsCalc(SQLValue **record, int32 groupCount, SQLValue* aggFunctionsRunTotals, int8* aggFunctionsCodes, - int32* aggFunctionsParamCols, int32* aggFunctionsRealParamCols, int32 aggFunctionsColsCount, int8* columnTypes, - int32* groupCountCols) -{ - TRACE("endAggFunctionsCalc") - int32 j = aggFunctionsColsCount, - colIndex, - realColIndex, - sqlAggFunction, - colType; - SQLValue* aggValue; - SQLValue* value; - - while (--j >= 0) // Concludes the calculation of the aggregate functions running totals. - { - colIndex = aggFunctionsParamCols[j]; - realColIndex = aggFunctionsRealParamCols[j]; - aggValue = &aggFunctionsRunTotals[j]; - value = record[colIndex]; - - switch (sqlAggFunction = aggFunctionsCodes[j]) - { - case FUNCTION_AGG_AVG: - { - if (groupCountCols[j] != 0) - value->asDouble = aggValue->asDouble/groupCountCols[j]; - aggValue->asDouble = 0; - break; - } - - // juliana@226_5: the aggregation functions MAX() and MIN() now work for CHAR, VARCHAR, CHAR NOCASE, and VARCHAR NOCASE column types. - case FUNCTION_AGG_MAX: - case FUNCTION_AGG_MIN: - { - switch (colType = columnTypes[realColIndex]) // Checks the type of the column. - { - case SHORT_TYPE: - value->asShort = aggValue->asShort; - break; - - case FLOAT_TYPE: - value->asFloat = aggValue->asFloat; - break; - - case DATE_TYPE: //rnovais@567_2 - case INT_TYPE: - value->asInt = aggValue->asInt; - break; - - case LONG_TYPE: - value->asLong = aggValue->asLong; - break; - - case DOUBLE_TYPE: - value->asDouble = aggValue->asDouble; - break; - - case DATETIME_TYPE: //rnovais@567_2 - value->asDate = aggValue->asDate; - value->asTime = aggValue->asTime; - break; - - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - // juliana@227_12: corrected a possible bug with MAX() and MIN() with strings. - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - { - xmemmove(value->asChars, aggValue->asChars, (value->length = aggValue->length) << 1); - value->asInt = aggValue->asInt; - value->asBlob = aggValue->asBlob; - } - } - break; - } - - case FUNCTION_AGG_COUNT: - { - value->asInt = groupCount; - break; - } - - case FUNCTION_AGG_SUM: - { - value->asDouble = aggValue->asDouble; - aggValue->asDouble = 0; - } - } - } -} - -/** - * Creates a temporary table that stores only an integer value. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param intValue The value to be put in the table. - * @param colName The column name of the single table column. - * @return The table if the method executes correctlty; null, otherwise. - * @throws OutOfMemoryError If there is not enougth memory alloc memory. - */ -Table* createIntValueTable(Context context, TCObject driver, int32 intValue, CharP colName) -{ - TRACE("createIntValueTable") - Table* table = null; - SQLValue val; - SQLValue* record; - int32* colHash; - int8* colType; - int32* colSize; - - Heap heap = heapCreate(); - IF_HEAP_ERROR(heap) - { - heapDestroy(heap); - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return null; - } - - // Creates unitary arrays with a hash code, type, and size. - colHash = (int32*)TC_heapAlloc(heap, 4); - colType = (int8*)TC_heapAlloc(heap, 1); - colSize = (int32*)TC_heapAlloc(heap, 4); - - *colHash = TC_hashCode(colName); - *colType = INT_TYPE; - - val.asInt = intValue; - record = &val; - - // juliana@223_14: solved possible memory problems. - if ((table = driverCreateTable(context, driver, null, null, colHash, colType, colSize, null, null, NO_PRIMARY_KEY, NO_PRIMARY_KEY, null, 0, 1, - heap)) && writeRSRecord(context, table, &record)) - return table; - - heapDestroy(heap); - return null; -} - -/** - * Binds the column information of the underlying tables to the select clause. - * - * @param context The thread context where the function is being executed. - * @param clause The select clause. - * @return false if an error occurs; true, otherwise. - * @throws SQLParseException In case of an unknown or ambiguous column name, the parameter and the function data types are incompatible, or the total - * number of fields of the select exceeds the maximum. - */ -bool bindColumnsSQLSelectClause(Context context, SQLSelectClause* clause) // guich@512_2: added columnnames -{ - TRACE("bindColumnsSQLSelectClause") - SQLResultSetField** fieldList = clause->fieldList; - SQLResultSetTable** tableList = clause->tableList; - int32 tableListSize = clause->tableListSize, - fieldListSize = clause->fieldsCount, - i, - j, - index, - columnCount; - Hashtable htName2index = clause->htName2index; - SQLResultSetTable* rsTable; - SQLResultSetField* field; - CharP tableName; - Table* currentTable; - CharP* columnNames; - int8* columnTypes; - int32* columnHashes; - int32* columnSizes; - - if (!fieldListSize) // If the select clause has a wild card (is null), then expands the list using the column information from the given tables. - { - int32 count = 0, - position = 0; - Table* table; - - j = tableListSize; - while (--j >= 0) - count += tableList[j]->table->columnCount - 1; // Excludes the rowid. - - // juliana@250_7: now a select * will cause a SQLParseException if the total number of columns is more than 254. - if (count > MAXIMUMS) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_FIELDS_OVERFLOW)); - return false; - } - - clause->fieldsCount = count; - - count = 0; - j = -1; - while (++j < tableListSize) - { - table = (rsTable = tableList[j])->table; - tableName = rsTable->tableName; - i = 0; - columnCount = table->columnCount; - columnNames = table->columnNames; - columnHashes = table->columnHashes; - columnTypes = table->columnTypes; - columnSizes = table->columnSizes; - - while (++i < columnCount) // guich@503_10: changed loop to exclude the rowid. - { - // SQLResultSetField - guich@503_10: added -1 to exclude the rowid. - (field = fieldList[count++] = initSQLResultSetField(clause->heap))->alias = columnNames[i]; // guich@_512_2: stores the column name too. - field->aliasHashCode = field->tableColHashCode = columnHashes[i]; - field->dataType = (int8)columnTypes[i]; - field->size = columnSizes[i]; - field->tableColIndex = i; - field->table = table; - field->tableName = rsTable->tableName; - TC_htPut32(&htName2index, TC_hashCodeFmt("sss", tableName, ".", columnNames[i]), position); - TC_htPut32IfNew(&htName2index, columnHashes[i], position++); - } - } - } - else - { - int32 hashAliasTableName, - hash1, - auxIndex, - aggFunctionType, - dtFunctionType, - sqlFunction; - bool foundFirst; - CharP fieldName; - Table* auxTable; - SQLResultSetTable* rsTableAux; - SQLResultSetField* param;; - - i = -1; - while (++i < fieldListSize) // Binds the listed colums to the table. - { - index = -1; - currentTable = null; - rsTable = null; - - // Aggregation functions (count() doesn't have a column name yet). - if (!(field = fieldList[i])->isAggregatedFunction || field->sqlFunction != FUNCTION_AGG_COUNT) - { - if (field->tableName) // Checks the names. - { - hashAliasTableName = TC_hashCode(field->tableName); - - // Verifies if it is a valid table name. - currentTable = null; - j = tableListSize; - while (--j >= 0) - if (tableList[j]->aliasTableNameHashCode == hashAliasTableName) - { - currentTable = (rsTable = tableList[j])->table; - break; - } - - if (!currentTable - || ((index = TC_htGet32Inv(¤tTable->htName2index, hash1 = TC_hashCode(fieldName = field->tableColName))) < 0)) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), field->alias); - return false; - } - - TC_htPut32(&htName2index, field->aliasHashCode, i); - if (field->aliasHashCode != TC_hashCodeFmt("sss", tableName = field->tableName, ".", fieldName)) // Used an explicit alias. - TC_htPut32(&htName2index, TC_hashCodeFmt("sss", tableName, ".", field->alias), i); - else if (TC_htGet32Inv(&htName2index, hash1) < 0) // Stores the name of the field once; only the first. - TC_htPut32(&htName2index, hash1, i); - - } - else // Verifies if the column name in the field list is ambiguous. - { - rsTable = null; - foundFirst = false; - currentTable = auxTable = null; - - j = tableListSize; - while (--j >= 0) - { - if ((auxIndex = TC_htGet32Inv(&(auxTable = (rsTableAux = tableList[j])->table)->htName2index, field->tableColHashCode)) >= 0) - { - if (foundFirst) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_AMBIGUOUS_COLUMN_NAME), field->alias); - return false; - } - else - { - foundFirst = true; - index = auxIndex; - TC_htPut32(&htName2index, TC_hashCodeFmt("sss", tableList[j]->tableName, ".", field->alias), i); - TC_htPut32(&htName2index, field->aliasHashCode, i); - - // juliana@252_4: corrected the fact that a field used in a function can't be fetched using only the name of the field - // unless it is also in the select field list. - if (field->sqlFunction == FUNCTION_DT_NONE) - TC_htPut32(&htName2index, field->tableColHashCode, i); - - currentTable = auxTable; - rsTable = rsTableAux; - } - } - } - if (!foundFirst) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_UNKNOWN_COLUMN), field->alias); - return false; - } - } - } - - if (!field->isVirtual) // If the field is not virtual, it needs to be mapped directly to the underlying table. - { - field->dataType = (int8)currentTable->columnTypes[field->tableColIndex = index]; - field->size = currentTable->columnSizes[index]; - field->table = currentTable; - field->tableName = rsTable->tableName; - TC_htPut32(&htName2index, field->aliasHashCode, i); - } - - // rnovais@568_10 - else if (field->isAggregatedFunction || field->isDataTypeFunction) // If it is an aggregated or data type function, maps its parameter. - { - param = field->parameter; - aggFunctionType = dtFunctionType = UNDEFINED_TYPE; // initialized with an UNDEFINED number. - sqlFunction = field->sqlFunction; - field->table = currentTable; - TC_htPut32(&htName2index, field->aliasHashCode, i); - - if (field->isAggregatedFunction) // Stores the correct function code. - aggFunctionType = aggregateFunctionsTypes[sqlFunction]; - else - dtFunctionType = dataTypeFunctionsTypes[sqlFunction]; - - if (param) - { - param->dataType = (int8)currentTable->columnTypes[param->tableColIndex = index]; - param->size = currentTable->columnSizes[index]; - - if (field->isAggregatedFunction) // rnovais@568_10 - { - - // Check if the parameter and aggregated function - // data types are compatible. - if (param->dataType == CHARS_TYPE - && (aggFunctionType == INT_TYPE || aggFunctionType == DOUBLE_TYPE)) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DATA_TYPE_FUNCTION), - (sqlFunction == FUNCTION_AGG_COUNT)? "count": (sqlFunction == FUNCTION_AGG_MAX)? "max" - : (sqlFunction == FUNCTION_AGG_MIN)? "min" : (sqlFunction == FUNCTION_AGG_AVG)? "avg" : (sqlFunction == FUNCTION_AGG_SUM)? "sum": ""); - return false; - } - - // For aggregated functions, if the function does not have a defined data type, it inherits the parameter size and type. - if (aggFunctionType == UNDEFINED_TYPE) - { - field->dataType = param->dataType; - field->size = param->size; - } - } - else - { - field->size = param->size; // rnovais@570_1 - - // rnovais@570_5: if UNDEFINED the datatype will be the same of the field thus, it is possible to have functions that can be - // applyed to diferents fieds type. e.g. ABS(int) returns int, ABS(double) returns double, etc. - if (dtFunctionType == UNDEFINED_TYPE) - field->dataType = param->dataType; - - // rnovais@568_10: checks if the parameter and the data type function data types are compatible. - if (!bindFunctionDataType(param->dataType, sqlFunction)) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DATA_TYPE_FUNCTION), - dataTypeFunctionsName(sqlFunction)); - return false; - } - } - } - } - } - - } - clause->htName2index = htName2index; - return true; -} - -/** - * Remaps a table column names, so it uses the alias names of the given field list, instead of the original names. - * - * @param context The thread context where the function is being executed. - * @param table The result set table. - * @param fieldsList The field list of the select clause. - * @param fieldsCount The number of fields of the select clause. - * @return false if an error occurs; true, otherwise. - * @throw OutOfMemoryError If a memory allocation fails. - */ -bool remapColumnsNames2Aliases(Context context, Table* table, SQLResultSetField** fieldsList, int32 fieldsCount) -{ - TRACE("remapColumnsNames2Aliases") - Hashtable* tableName2Index = &table->htName2index; - int32* columnHashes = table->columnHashes; - SQLResultSetField* field; - Heap heap = table->heap; - - if (!table->columnNames) // previously created? don't overrite it - { - IF_HEAP_ERROR(heap) // juliana@223_14: solved possible memory problems. - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - return false; - } - - table->columnNames = (CharP*)TC_heapAlloc(heap, fieldsCount * TSIZE); - - while (--fieldsCount >= 0) - { - table->columnNames[fieldsCount] = (field = fieldsList[fieldsCount])->alias; - - if (columnHashes[fieldsCount] == field->aliasHashCode) // Replaces the original mapping, if necessary. - continue; - if (!TC_htPut32(tableName2Index, field->aliasHashCode, fieldsCount)) // Already replaces old values. - return false; - } - } - return true; -} - -/** - * Writes the records of a result set to a table. - * - * @param context The thread context where the function is being executed. - * @param list The result set list, one for each table in the from field. - * @param numTables The number of tables of the select. - * @param rs2TableColIndexes The mapping between result set and table columns. - * @param selectClause The select clause of the query. - * @param columnIndexesTables Has the indices of the tables for each resulting column. - * @param whereClauseType Indicates the where clause is an AND or an OR. - * @param heap A heap to allocate temporary structures. - * @return The total number of records added to the table or -1 if an error occurs. - */ -int32 writeResultSetToTable(Context context, ResultSet** list, int32 numTables, Table* table, int16* rs2TableColIndexes, - SQLSelectClause* selectClause, size_t* columnIndexesTables, int32 whereClauseType, Heap heap) -{ - TRACE("writeResultSetToTable") - int32 count = table->columnCount; - SQLValue** values = (SQLValue**)TC_heapAlloc(heap, count * TSIZE); - ResultSet* resultSet = *list; - PlainDB* tempDB = &table->db; - PlainDB* selectDB = &(*selectClause->tableList)->table->db; - XFile* memoryDB = &tempDB->db; - IntVector rowsBitmap = resultSet->rowsBitmap; - Table* rsTable = resultSet->table; - int8* rsTypes = rsTable->columnTypes; - int32* sizes = table->columnSizes; - int32* rsSizes = rsTable->columnSizes; - int16* items = rs2TableColIndexes? rs2TableColIndexes : null; - int32 countSelectedField = selectClause->fieldsCount, // rnovais@568_10: when it has an order by table.columnCount = selectClause.fieldsCount + 1. - i, - j, - totalRecords = 0, - rowSize = tempDB->rowSize, - rsCount = rsTable->columnCount, - bytes = NUMBEROFBYTES(rsCount), - dbSize, - dboSize, - size = 0; - uint8* nulls0 = table->columnNulls; - uint8* rsNulls0 = rsTable->columnNulls; - uint8* buffer = rsTable->db.basbuf + rsTable->columnOffsets[rsCount]; - - j = table->columnCount; - while (--j >= 0) - if (sizes[j]) - size += 4 + TSIZE; - - // juliana@223_14: solved possible memory problems. - // juliana@223_9: improved Litebase temporary table allocation on Windows 32, Windows CE, Palm, iPhone, and Android. - // No indices and no where clause: allocs all the records space in the temporary .db. - if (!resultSet->whereClause && !resultSet->rowsBitmap.size) - { - if (!mfGrowTo(context, memoryDB, (tempDB->rowAvail = 1 + selectDB->rowCount - (*selectClause->tableList)->table->deletedRowsCount) * rowSize) - || !mfGrowTo(context, &tempDB->dbo, tempDB->rowAvail * size)) - goto error; - } - else // Uses colected statistics. - { - LOCKVAR(parser); - if (muGet(&memoryUsage, selectClause->sqlHashCode, &dbSize, &dboSize)) - { - if (!mfGrowTo(context, memoryDB, dbSize)) - goto error; - tempDB->rowAvail = dbSize / rowSize; - if (!mfGrowTo(context, &tempDB->dbo, dboSize)) - goto error; - } - UNLOCKVAR(parser); - } - if (!tempDB->rowAvail && rowsBitmap.size) // Uses the indices if the other approachs can't be useful. - { - int32 numberOfBits = bitCount(rowsBitmap.items, rowsBitmap.length) + 1; - - if (numberOfBits > 0) - { - if (!mfGrowTo(context, memoryDB, (tempDB->rowAvail = 1 + numberOfBits) * rowSize) - || !mfGrowTo(context, &tempDB->dbo, tempDB->rowAvail * size)) - goto error; - } - } - - if (numTables == 1) - { - int32 colIndex; - - resultSet->pos = -1; - i = count; - - while (--i >= 0) // Allocates the record before using them. - { - if ((colIndex = (items)? items[i] : i) != -1) - { - values[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - if ((rsTypes[colIndex] == CHARS_TYPE || rsTypes[colIndex] == CHARS_NOCASE_TYPE)) - values[i]->asChars = (JCharP)TC_heapAlloc(heap, (rsSizes[colIndex] << 1) + 2); - } - } - - while (getNextRecord(context, resultSet, heap)) // No preverify needed. - { - j = 0; - i = -1; - - xmemmove(rsNulls0, buffer, bytes); // Reads the bytes of the nulls. - - while (++i < count) // Gets the values of the result set columns. - { - colIndex = items? items[i] : i; - - // For columns that do no map directly to the underlying table of the result set, just skips the reading. - if (colIndex != -1 && isBitUnSet(rsNulls0, colIndex) && !getTableColValue(context, resultSet, colIndex, values[i])) // juliana@220_3 - goto error; - - if (colIndex != -1) - setBit(nulls0, i, isBitSet(rsNulls0, colIndex)); // Sets the null values of tempTable. - else - setBitOff(nulls0, i); - - if (j < countSelectedField) // rnovais@568_10 - j++; - } - - if (writeRSRecord(context, table, values)) // Writes the record. - totalRecords++; - else - goto error; - } - } - else // Join. - { - size_t* columnIndexesTablesItems = columnIndexesTables; - - i = numTables; - while (--i >= 0) - list[i]->indexes = newIntVector(count, heap); - - i = count; - while (--i >= 0) - if (items[i] != -1) // count(*) - { - j = numTables; - while (--j >= 0) - if ((Table*)columnIndexesTablesItems[i] == list[j]->table) - { - IntVectorAdd(&list[j]->indexes, i); - break; - } - } - totalRecords = performJoin(context, list, numTables, table, rs2TableColIndexes, values, whereClauseType, heap); - } - - freeResultSet(resultSet); - return totalRecords; - -error: - freeResultSet(resultSet); - return -1; -} - -/** - * Counts the number of ON bits. - * - * @param elements The array where the bits will be counted. - * @param length The array length. - * @return The number of on bits. - */ -int32 bitCount(int32* elements, int32 length) -{ - TRACE("bitCount") - int32 count = 0, - value; - uint8* bitElems = (uint8*)elements; - length *= 4; - - while (--length >= 0) - { - value = *bitElems++; - count += bitsInNibble[value & 0xF] + bitsInNibble[(value >> 4) & 0xF]; - } - return count; -} - -/** - * Executes a join operation. - * - * @param context The thread context where the function is being executed. - * @param list The list of the result sets. - * @param numTables The number of tables of the select. - * @param table The result set table. - * @param rs2TableColIndexes The mapping between result set and table columns. - * @param values The record to be joined with. - * @param whereClauseType The type of operation used: AND or OR. - * @param heap A heap to allocate temporary structures. - * @return The number of records written to the temporary table or -1 if an error occurs. - */ -int32 performJoin(Context context, ResultSet** list, int32 numTables, Table* table, int16* rs2TableColIndexes, SQLValue** values, - int32 whereClauseType, Heap heap) -{ - TRACE("performJoin") - int32 currentIndexTable = 0, - totalRecords = 0, - ret = NO_RECORD, - length = table->columnCount, - colIndex, - position, - rsCount; - bool bitSet; - uint8 verifyWhereCondition[MAXIMUMS]; - IntVector indexes; - ResultSet* currentRs; - Table* rsTable; - int8* types = table->columnTypes; - int32* sizes = table->columnSizes; - uint8* nulls0 = table->columnNulls; - uint8* rsNulls0; - - xmemset(verifyWhereCondition, true, numTables); - - while (--length >= 0) // Allocates the necessary records before using them. - { - values[length] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - if (types[length] == CHARS_TYPE || types[length] == CHARS_NOCASE_TYPE) - values[length]->asChars = (JCharP)TC_heapAlloc(heap, (sizes[length] << 1) + 2); - } - - while (currentIndexTable >= 0) - { - currentRs = list[currentIndexTable]; - - switch (ret = getNextRecordJoin(context, currentIndexTable, verifyWhereCondition[currentIndexTable], numTables, whereClauseType, list, heap)) - { - case VALIDATION_RECORD_OK: - case VALIDATION_RECORD_INCOMPLETE: - { - length = (indexes = currentRs->indexes).size; - rsCount = (rsTable = currentRs->table)->columnCount; - xmemmove(rsNulls0 = rsTable->columnNulls, rsTable->db.basbuf + rsTable->columnOffsets[rsCount], NUMBEROFBYTES(rsCount)); - - while (--length >= 0) // Fills the data of the current ResultSet. - { - position = indexes.items[length]; - - // If rs2TableColIndexes == null, it indicates that the result set and the table have the same sequence of columns. - colIndex = rs2TableColIndexes? rs2TableColIndexes[position] : length; - - // For columns that do no map directly to the underlying table of the result set, just skips the reading. - bitSet = isBitSet(rsNulls0, colIndex); - - // If it is null, just skips. - if ((colIndex != -1) && !bitSet && !getTableColValue(context, currentRs, colIndex, values[position])) // juliana@220_3 - return -1; - - if (colIndex != -1) - setBit(nulls0, position, bitSet); // Sets the null values from the temporary table. - else - setBitOff(nulls0, position); - } - if (ret == VALIDATION_RECORD_OK) - { - if (currentIndexTable < numTables - 1) // Goes to the next table. - { - currentIndexTable++; - verifyWhereCondition[currentIndexTable] = false; - } - else // It is the last resultSet, so stores the data. - { - if (writeRSRecord(context, table, values)) // Writes the record. - totalRecords++; - else - return -1; - } - } - else // VALIDATION_RECORD_INCOMPLETE - { - currentIndexTable++; - verifyWhereCondition[currentIndexTable] = true; - } - break; - } - case NO_RECORD: - { - currentIndexTable--; - currentRs->pos = -1; // Restarts the current resultset to the next iteration. - } - } - } - return totalRecords; -} - -/** - * Gets the next record to perform the join operation. - * - * @param context The thread context where the function is being executed. - * @param rsIndex The index of the result set of the list used to get the next record. - * @param verifyWhereCondition Indicates if the where clause needs to be verified. - * @param totalRs The number of result sets (tables used in the join) in the result set list. - * @param whereClauseType The type of expression in the where clause (OR or AND). - * @param rsList The list of the result sets. - * @param heap A heap to allocate temporary structures. - * @return VALIDATION_RECORD_OK, NO_RECORD, VALIDATION_RECORD_NOT_OK, - * VALIDATION_RECORD_INCOMPLETE, or -1 if an error occurs. - */ -int32 getNextRecordJoin(Context context, int32 rsIndex, bool verifyWhereCondition, int32 totalRs, int32 whereClauseType, ResultSet** rsList, - Heap heap) -{ - TRACE("getNextRecordJoin") - ResultSet* resultSet = rsList[rsIndex]; - PlainDB* plainDB = &resultSet->table->db; - uint8* basbuf = plainDB->basbuf; - IntVector rowsBitmap = (resultSet->auxRowsBitmap.size > 0)? resultSet->auxRowsBitmap : resultSet->rowsBitmap; - SQLBooleanClause* whereClause = resultSet->whereClause; - int32 rowCountLess1 = plainDB->rowCount - 1, - ret; - - // Desired rows partially computed using the indexes? - if (rowsBitmap.size && verifyWhereCondition) - { - int32 position; - - if (resultSet->pos < rowCountLess1) - { - if (!resultSet->whereClause || resultSet->auxRowsBitmap.size) - { - // No WHERE clause. Just returns the rows marked in the bitmap. - while ((position = findNextBitSet(&rowsBitmap, resultSet->pos + 1)) != -1 && position <= rowCountLess1) - { - if (plainRead(context, plainDB, resultSet->pos = position)) - { - if (recordNotDeleted(basbuf)) // juliana@230_45: join should not take deleted rows into consideration. - { - if (resultSet->auxRowsBitmap.size && verifyWhereCondition && whereClause) - { - whereClause->resultSet = resultSet; - return booleanTreeEvaluateJoin(context, whereClause->expressionTree, rsList, totalRs, heap); - } - if (whereClauseType == WC_TYPE_AND_DIFF_RS) - return (totalRs == resultSet->indexRs + 1)? VALIDATION_RECORD_OK : VALIDATION_RECORD_INCOMPLETE; - return VALIDATION_RECORD_OK; - } - } - else - return -1; - } - } - else - { - // With a remaining WHERE clause there are 2 situations. - // 1) The relationship between the bitmap and the WHERE clause is an AND relationship. - // 2) The relationship between the bitmap and the WHERE clause is an OR relationship. - if (resultSet->rowsBitmapBoolOp == OP_BOOLEAN_AND) - { - // AND case - Walks through the bits that are set in the bitmap and checks if the rows satisfy the where clause. - while ((position = findNextBitSet(&rowsBitmap, resultSet->pos + 1)) != -1 && position <= rowCountLess1) - if (plainRead(context, plainDB, resultSet->pos = position)) - { - if (recordNotDeleted(basbuf)) // juliana@230_45: join should not take deleted rows into consideration. - { - whereClause->resultSet = resultSet; - return booleanTreeEvaluateJoin(context, whereClause->expressionTree, rsList, totalRs, heap); - } - } - else - return -1; - } - else - { - // OR case - Walks through all records. If the corresponding bit is set in the bitmap, does not need to evaluate WHERE clause. - // Otherwise, checks if row satisfies WHERE clause. - while (resultSet->pos < rowCountLess1 && plainRead(context, plainDB, ++resultSet->pos)) - { - if (IntVectorisBitSet(&rowsBitmap, resultSet->pos)) - return VALIDATION_RECORD_OK; - whereClause->resultSet = resultSet; - if (recordNotDeleted(basbuf) - && (ret = booleanTreeEvaluateJoin(context, whereClause->expressionTree, rsList, totalRs, heap)) != VALIDATION_RECORD_NOT_OK) - return ret; - } - } - } - } - return NO_RECORD; - } - else - { - while (resultSet->pos < rowCountLess1 && plainRead(context, plainDB, ++resultSet->pos)) - if (recordNotDeleted(basbuf)) - { - if (!(whereClause && verifyWhereCondition)) - return VALIDATION_RECORD_OK; - - // juliana@213_2: corrected a bug that could make joins not work with ORs using indices. - whereClause->resultSet = resultSet; - if ((ret = booleanTreeEvaluateJoin(context, whereClause->expressionTree, rsList, totalRs, heap)) == VALIDATION_RECORD_NOT_OK - && resultSet->whereClause->appliedIndexesBooleanOp == OP_BOOLEAN_OR) - while (++rsIndex < totalRs) - if (rsList[rsIndex]->auxRowsBitmap.size || rsList[rsIndex]->rowsBitmap.size) - return VALIDATION_RECORD_INCOMPLETE; - return ret; - } - return NO_RECORD; - } -} - -/** - * Evaluates an expression tree for a join. - * - * @param context The thread context where the function is being executed. - * @param tree The expression tree to be evaluated. - * @param rsList The list of the result sets. - * @param totalRs The number of result sets (tables used in the join) in the result set list. - * @param heap A heap to allocate temporary structures. - * @return VALIDATION_RECORD_OK, NO_RECORD, VALIDATION_RECORD_NOT_OK, - * VALIDATION_RECORD_INCOMPLETE, or -1 if an error occurs. - */ -int32 booleanTreeEvaluateJoin(Context context, SQLBooleanClauseTree* tree, ResultSet** rsList, int32 totalRs, Heap heap) -{ - TRACE("booleanTreeEvaluateJoin") - ResultSet* resultSet = tree->booleanClause->resultSet; - SQLBooleanClauseTree* leftTree = tree->leftTree; - SQLBooleanClauseTree* rightTree = tree->rightTree; - int32 indexRs = resultSet->indexRs, - indexTree = tree->indexRs; - - if (indexTree >= 0) // AND, OR and BOOLEAN_NOT have index = -1. - { - if (indexTree < indexRs) // It was avaliated before and can return true. - return VALIDATION_RECORD_INCOMPLETE_OK; - if (indexTree > indexRs) // juliana@211_5: solved a bug with joins which would return more answers than desired. - { - if (tree->bothAreIdentifier && leftTree->indexRs == indexRs) // Fills leftTree.value. - { - SQLValue* valueJoin = &leftTree->valueJoin; - ResultSet* rsBag = rsList[rightTree->indexRs]; - int32 boolOp = rsBag->whereClause->appliedIndexesBooleanOp; - IntVector auxRowsBitmap; - - if (!valueJoin->asChars) - valueJoin->asChars = (JCharP)TC_heapAlloc(heap, 2 * resultSet->table->columnSizes[leftTree->colIndex] + 2); - if (!getOperandValue(context, leftTree, valueJoin)) - return -1; - if (rightTree->hasIndex && boolOp <= 1) - { - // juliana@225_13: join now behaves well with functions in columns with an index. - SQLBooleanClause* booleanClause = tree->booleanClause; - SQLResultSetField** fieldList = booleanClause->fieldList; - int32 i = booleanClause->fieldsCount; - - while (--i >= 0) - if (fieldList[i]->tableColIndex == rightTree->colIndex && fieldList[i]->isDataTypeFunction) - return VALIDATION_RECORD_INCOMPLETE; - - // Despite this is a join the parameter 'false' is sent because this is a simple index calculation. - if (!computeIndex(context, rsList, totalRs, false, rightTree->indexRs, valueJoin, tree->operandType, rightTree->colIndex, heap)) - return -1; - - // juliana@230_39: join now can be much faster if the query is smartly written. - auxRowsBitmap = rsBag->auxRowsBitmap; - if (rsBag->rowsBitmap.items && auxRowsBitmap.items && boolOp == 1) - { - mergeBitmaps(&auxRowsBitmap, &rsBag->rowsBitmap, 1); - if (!bitCount(auxRowsBitmap.items, auxRowsBitmap.size)) - return VALIDATION_RECORD_NOT_OK; - } - - } - } - return VALIDATION_RECORD_INCOMPLETE; - } - } - - // The indexes match, so compare the records. - switch (tree->operandType) // Checks what is the operand type of the tree. - { - // Relational operand. - case OP_REL_EQUAL: - case OP_REL_DIFF: - case OP_REL_GREATER: - case OP_REL_LESS: - case OP_REL_GREATER_EQUAL: - case OP_REL_LESS_EQUAL: - switch (tree->valueType) // Calls the right operation accordingly to the values type. - { - case SHORT_TYPE: - case INT_TYPE: - case LONG_TYPE: - case FLOAT_TYPE: - case DOUBLE_TYPE: - case DATE_TYPE: - case DATETIME_TYPE: - return compareNumericOperands(context, tree)? VALIDATION_RECORD_OK: VALIDATION_RECORD_NOT_OK; - return -1; - case CHARS_TYPE: - return compareStringOperands(context, tree, false, heap)? VALIDATION_RECORD_OK: VALIDATION_RECORD_NOT_OK; - case CHARS_NOCASE_TYPE: - return compareStringOperands(context, tree, true, heap)? VALIDATION_RECORD_OK: VALIDATION_RECORD_NOT_OK; - } - - // juliana@201_4: joins with like were returning the opposite result. - // Relational operand. - case OP_PAT_MATCH_LIKE: - case OP_PAT_MATCH_NOT_LIKE: - return matchStringOperands(context, tree, tree->valueType == CHARS_NOCASE_TYPE, heap)? VALIDATION_RECORD_OK: VALIDATION_RECORD_NOT_OK; - - case OP_BOOLEAN_AND: // AND connector. - if (leftTree && rightTree) // Expects both trees not be null. - { - switch (booleanTreeEvaluateJoin(context, leftTree, rsList, totalRs, heap)) - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_NOT_OK; - case VALIDATION_RECORD_INCOMPLETE: - if (booleanTreeEvaluateJoin(context, rightTree, rsList, totalRs, heap) == VALIDATION_RECORD_NOT_OK) // Verifies the right branch. - return VALIDATION_RECORD_NOT_OK; - - // All other results return incomplete because the left side has returned incomplete. - return VALIDATION_RECORD_INCOMPLETE; - case VALIDATION_RECORD_INCOMPLETE_OK: - case VALIDATION_RECORD_OK: - switch (booleanTreeEvaluateJoin(context, rightTree, rsList, totalRs, heap)) // The left side returned true, so verifies the right branch. - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_NOT_OK; - case VALIDATION_RECORD_INCOMPLETE_OK: - case VALIDATION_RECORD_OK: - return VALIDATION_RECORD_OK; // Both sides returns true. - case VALIDATION_RECORD_INCOMPLETE: - - // If the right side returns incomplete, incomplete must be returned, despite the left side returned OK. - return VALIDATION_RECORD_INCOMPLETE; - } - } - } - - case OP_BOOLEAN_OR: // OR connector. - if (leftTree && rightTree) // Expects both trees to be not null. - { - switch (booleanTreeEvaluateJoin(context, leftTree, rsList, totalRs, heap)) - { - case VALIDATION_RECORD_OK: - return VALIDATION_RECORD_OK; // Short circuit. - case VALIDATION_RECORD_INCOMPLETE_OK: - switch (booleanTreeEvaluateJoin(context, rightTree, rsList, totalRs, heap)) // Verifies the right branch. - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_INCOMPLETE_OK; // juliana@263_1: corrected a very old bug in a join with OR. - case VALIDATION_RECORD_INCOMPLETE: - return VALIDATION_RECORD_INCOMPLETE; - case VALIDATION_RECORD_OK: - case VALIDATION_RECORD_INCOMPLETE_OK: - return VALIDATION_RECORD_OK; // The right side returned true. - } - case VALIDATION_RECORD_INCOMPLETE: - switch (booleanTreeEvaluateJoin(context, rightTree, rsList, totalRs, heap)) // Verifies the right branch. - { - case VALIDATION_RECORD_NOT_OK: - case VALIDATION_RECORD_INCOMPLETE: - return VALIDATION_RECORD_INCOMPLETE; - case VALIDATION_RECORD_OK: - case VALIDATION_RECORD_INCOMPLETE_OK: - return VALIDATION_RECORD_OK; // The right side returned true. - } - case VALIDATION_RECORD_NOT_OK: - // The left side returned false, so continues verifing the right branch. - switch (booleanTreeEvaluateJoin(context, rightTree, rsList, totalRs, heap)) - { - case VALIDATION_RECORD_NOT_OK: - return VALIDATION_RECORD_NOT_OK; - - // juliana@270_21: solved a very old join problem when using OR and false constants comparison which would make the join - // return no results. - case VALIDATION_RECORD_INCOMPLETE_OK: - return VALIDATION_RECORD_INCOMPLETE_OK; - - case VALIDATION_RECORD_OK: - return VALIDATION_RECORD_OK; // The right side returned true. - case VALIDATION_RECORD_INCOMPLETE: - return VALIDATION_RECORD_INCOMPLETE; - } - } - } - - // juliana@214_4: nots were removed. - - // IS and IS NOT. - case OP_PAT_IS: - case OP_PAT_IS_NOT: - return compareNullOperands(tree)? VALIDATION_RECORD_OK : VALIDATION_RECORD_NOT_OK; - } - - return VALIDATION_RECORD_INCOMPLETE; -} - -/** - * Calculates aggregation functions. - * - * @param context The thread context where the function is being executed. - * @param record The record of the values to be used in the calculation. - * @param nullsRecord The values of the record that are null. - * @param aggFunctionsRunTotals The current totals for the aggregation functions. - * @param aggFunctionsCodes The codes of the used aggregation functions. - * @param aggFunctionsParamCols The columns that use aggregation functions. - * @param aggFunctionsColsCount The number of columns that use aggregation functions. - * @param columnTypes The types of the columns. - * @param groupCountCols The columns that use count. - */ -void performAggFunctionsCalc(Context context, SQLValue** record, uint8* nullsRecord, SQLValue* aggFunctionsRunTotals, int8* aggFunctionsCodes, - int32* aggFunctionsParamCols, int32 aggFunctionsColsCount, int8* columnTypes, int32* groupCountCols) -{ - TRACE("performAggFunctionsCalc") - int32 i = aggFunctionsColsCount, - colIndex, - sqlAggFunction, - colType; - SQLValue* aggValue; - SQLValue* value; - - while (--i >= 0) // Performs the calculation of the aggregation functions. - { - if (!aggFunctionsCodes[i]) - { - groupCountCols[i]++; - continue; - } - if ((colIndex = aggFunctionsParamCols[i]) < 0) - continue; - - if (isBitSet(nullsRecord, colIndex)) - continue; - - groupCountCols[i]++; - - colType = columnTypes[colIndex]; - aggValue = &aggFunctionsRunTotals[i]; - value = record[colIndex]; - - switch (sqlAggFunction = aggFunctionsCodes[i]) - { - case FUNCTION_AGG_AVG: - case FUNCTION_AGG_SUM: - { - switch (colType) // Checks the type of the column. - { - case SHORT_TYPE: - aggValue->asDouble += value->asShort; - break; - - case FLOAT_TYPE: - aggValue->asDouble += value->asFloat; - break; - - case INT_TYPE: - aggValue->asDouble += value->asInt; - break; - - case LONG_TYPE: - aggValue->asDouble += value->asLong; - break; - - case DOUBLE_TYPE: - aggValue->asDouble += value->asDouble; - break; - - case DATE_TYPE: // rnovais@567_2 - case DATETIME_TYPE: - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_SUM_AVG_WITH_DATE_DATETIME)); - return; - } - break; - } - - // juliana@226_5: the aggregation functions MAX() and MIN() now work for CHAR, VARCHAR, CHAR NOCASE, and VARCHAR NOCASE column types. - case FUNCTION_AGG_MAX: - { - switch (colType) // Checks the type of the column. - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - if (groupCountCols[i] == 1 - || str16CompareTo(aggValue->asChars, value->asChars, aggValue->length, value->length, colType) < 0) - { - xmemmove(aggValue->asChars, value->asChars, (aggValue->length = value->length) << 1); - aggValue->asInt = value->asInt; - aggValue->asBlob = value->asBlob; - } - break; - - case SHORT_TYPE: - if (groupCountCols[i] == 1 || aggValue->asShort < value->asShort) - aggValue->asShort = value->asShort; - break; - - case FLOAT_TYPE: - if (groupCountCols[i] == 1 || aggValue->asFloat < value->asFloat) - aggValue->asFloat = value->asFloat; - break; - - case DATE_TYPE: // rnovais@567_2 - case INT_TYPE: - if (groupCountCols[i] == 1 || aggValue->asInt < value->asInt) - aggValue->asInt = value->asInt; - break; - - case LONG_TYPE: - if (groupCountCols[i] == 1 || aggValue->asLong < value->asLong) - aggValue->asLong = value->asLong; - break; - - case DOUBLE_TYPE: - if (groupCountCols[i] == 1 || aggValue->asDouble < value->asDouble) - aggValue->asDouble = value->asDouble; - break; - - case DATETIME_TYPE: // rnovais@567_2 - if (groupCountCols[i] == 1) - { - aggValue->asDate = value->asDate; - aggValue->asTime = value->asTime; - } - else if (aggValue->asDate <= value->asDate) - { - if (aggValue->asDate < value->asDate || aggValue->asTime < value->asTime) // The date or time is smaller. - { - aggValue->asDate = value->asDate; - aggValue->asTime = value->asTime; - } - } - break; - } - break; - } - - // juliana@226_5: the aggregation functions MAX() and MIN() now work for CHAR, VARCHAR, CHAR NOCASE, and VARCHAR NOCASE column types. - case FUNCTION_AGG_MIN: - { - switch (colType) // Checks the type of the column. - { - // juliana@226_9: strings are not loaded anymore in the temporary table when building result sets. - case CHARS_TYPE: - case CHARS_NOCASE_TYPE: - if (groupCountCols[i] == 1 - || str16CompareTo(aggValue->asChars, value->asChars, aggValue->length, value->length, colType) > 0) - { - xmemmove(aggValue->asChars, value->asChars, (aggValue->length = value->length) << 1); - aggValue->asInt = value->asInt; - aggValue->asBlob = value->asBlob; - } - break; - - case SHORT_TYPE: - if (groupCountCols[i] == 1 || aggValue->asShort > value->asShort) - aggValue->asShort = value->asShort; - break; - - case FLOAT_TYPE: - if (groupCountCols[i] == 1 || aggValue->asFloat > value->asFloat) - aggValue->asFloat = value->asFloat; - break; - - case DATE_TYPE: - case INT_TYPE: - if (groupCountCols[i] == 1 || aggValue->asInt > value->asInt) - aggValue->asInt = value->asInt; - break; - - case LONG_TYPE: - if (groupCountCols[i] == 1 || aggValue->asLong > value->asLong) - aggValue->asLong = value->asLong; - break; - - case DOUBLE_TYPE: - if (groupCountCols[i] == 1 || aggValue->asDouble > value->asDouble) - aggValue->asDouble = value->asDouble; - break; - - case DATETIME_TYPE: - if (groupCountCols[i] == 1) - { - aggValue->asDate = value->asDate; - aggValue->asTime = value->asTime; - } - else - if (aggValue->asDate >= value->asDate) - { - if (aggValue->asDate > value->asDate || aggValue->asTime > value->asTime) // The date is greater. - { - aggValue->asDate = value->asDate; - aggValue->asTime = value->asTime; - } - } - } - } - } - } -} - -// juliana@230_14: removed temporary tables when there is no join, group by, order by, and aggregation. -/** - * Calculates the answer of a select without aggregation, join, order by, or group by without using a temporary table. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set of the table. - * @param heap A heap to allocate temporary structures. - */ -void computeAnswer(Context context, ResultSet* resultSet, Heap heap) -{ - TRACE("computeAnswer") - int32 i; - Table* table = resultSet->table; - uint8* allRowsBitmap = table->allRowsBitmap; - - if (!resultSet->whereClause && !resultSet->rowsBitmap.size && !table->deletedRowsCount) - { - i = table->answerCount = table->db.rowCount; - while (--i >= 0) - setBitOn(allRowsBitmap, i); - } - else - { - i = 0; - while (getNextRecord(context, resultSet, heap)) // No preverify needed. - { - setBitOn(allRowsBitmap, resultSet->pos); - i++; - } - table->answerCount = i; - } -} - -// juliana@230_21: MAX() and MIN() now use indices on simple queries. -/** - * Finds the best index to use in a min() or max() operation. - * - * @param field The field which may have a min() or max() operation. - */ -void findMaxMinIndex(SQLResultSetField* field) -{ - TRACE("findMaxMinIndex") - Table* table = field->table; - int32 column = field->parameter? field->parameter->tableColIndex : field->tableColIndex, - i = table->numberComposedIndexes; - ComposedIndex** composedIndices = table->composedIndexes; - - if (table->columnIndexes[column]) // If the field has a simple index, uses it. - { - field->index = column; - field->isComposed = false; - } - else - while (--i >= 0) - if (*composedIndices[i]->columns == column) // Else, if the field is the first field of a composed index, uses it. - { - field->index = i; - field->isComposed = true; - break; - } - - if (i == -1) - field->index = -1; -} diff --git a/LitebaseSDK/src/native/parser/SQLSelectStatement.h b/LitebaseSDK/src/native/parser/SQLSelectStatement.h deleted file mode 100644 index 5684566f42..0000000000 --- a/LitebaseSDK/src/native/parser/SQLSelectStatement.h +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions to initialize, set, and process a select statement. - */ - -#ifndef LITEBASE_SQLSELECTSTATEMENT_H -#define LITEBASE_SQLSELECTSTATEMENT_H - -#include "Litebase.h" - -/** - * Creates and initializes a SQL select statement. - * - * @param parser The structure returned from the parsing process. - * @param isPrepared Indicates if the delete statement is from a prepared statement. - * @return A pointer to a SQLSelectStatement structure. - */ -SQLSelectStatement* initSQLSelectStatement(LitebaseParser* parser, bool isPrepared); - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param selectStmt A SQL select statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows DriverException If the parameter index is invalid. - */ -bool setNumericParamValueSel(Context context, SQLSelectStatement* selectStmt, int32 index, VoidP value, int32 type); - -/* - * Sets the value of a string parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param selectStmt A SQL select statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @thows DriverException If the parameter index is invalid. - * @return false if an error occurs; true, otherwise. - */ -bool setParamValueStringSel(Context context, SQLSelectStatement* selectStmt, int32 index, JCharP value, int32 length); - -/** - * Clears all parameter values of a prepared statement select. - * - * @param selectStmt A SQL select statement. - */ -void clearParamValuesSel(SQLSelectStatement* selectStmt); - -/** - * Checks if all parameters values are defined. - * - * @param selectStmt A SQL select statement. - * @return true, if all parameters values are defined; false otherwise. - */ -bool allParamValuesDefinedSel(SQLSelectStatement* selectStmt); - -/** - * Executes a select statement. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param selectStmt A SQL select statement. - * @return A result set returned by the query execution. - * @throws DriverException If the record can't be removed from the indices. - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -TCObject litebaseDoSelect(Context context, TCObject driver, SQLSelectStatement* selectStmt); - -/** - * Binds a select statement. - * - * @param context The thread context where the function is being executed. - * @param driver The Litebase connection. - * @param selectStmt A SQL select statement. - * @return true, if the statement was bound successfully; false otherwise. - */ -bool litebaseBindSelectStatement(Context context, TCObject driver, SQLSelectStatement* selectStmt); - -/** - * Tries to put as inner table a table that has an index used more often in the where clause, when the where clause has a comparison between - * fields from different tables. e.g.: select * from table1, table2 where table1.field1 = table2.field2 If only - * table1.field1 has index, changes the select to: select * from table2, table1 where table1.field1 = table2.field2. - * If both tables has the same level of index using, sorts them by the row count. - * - * @param selectStmt A SQL select statement. - */ -void orderTablesToJoin(SQLSelectStatement* selectStmt); - -/** - * Binds the SQLSelectStatement to the select clause tables. - * - * @param context The thread context where the function is being executed. - * @param selectStmt A SQL select statement. - * @return true if the statement could be corrected bound; false, otherwise. - */ -bool bindSelectStatement(Context context, SQLSelectStatement* selectStmt); - -/** - * Validates the SQLSelectStatement. - * - * @param context The thread context where the function is being executed. - * @param selectStmt The select statement to be validated. - * @return false if a SQLParseException occurs; true, otherwise. - * @throws SQLParseException If the order by and group by clauses do not match, if a query with group by is not well-formed, if there is a - * having clause without an aggregation, a field in the having clause is not in the select clause, there is no order by and there are aggregated - * functions mixed with real columns, or there is an aggregation with an order by clause and no group by clause. - */ -bool validateSelectStatement(Context context, SQLSelectStatement* selectStmt); - -/** - * Generates a table to store the result set. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param selectStmt The select statement to be validated. - * @return The temporary result set table or null if an error occurs. - * @throws OutOfMemoryError If there is not enougth memory alloc memory. - */ -Table* generateResultSetTable(Context context, TCObject driver, SQLSelectStatement* selectStmt); - -/** - * Generates a table to store the result set. - * - * @param context The thread context where the function is being executed. - * @param tableList The table list of the select. - * @param size The number of tables of the select. - * @param whereClause the where clause of the select. - * @param rsList Receives the temporary result set list. - * @param heap A heap to perform some memory allocations. - * @return falseif an error occurs when appling the indices; true, otherwise. - */ -bool createListResultSetForSelect(Context context, SQLResultSetTable** tableList, int32 size, SQLBooleanClause* whereClause, ResultSet** rsList, Heap heap); - -/** - * Generates an index bit map for a list of result sets. - * - * @param context The thread context where the function is being executed. - * @param rsList The list of result sets. - * @param size The number of tables of the select. - * @param hasComposedIndex Indicates if the table has a composed index. - * @param heap A heap to allocate temporary structures. - * @return true if the function executed correctly; false, otherwise. - */ -bool generateIndexedRowsMap(Context context, ResultSet** rsList, int32 size, bool hasComposedIndex, Heap heap); - -/** - * Finds the rows that satisfy the query clause using the indices. - * - * @param context The thread context where the function is being executed. - * @param rsList The result set list, one for each table. - * @param size The number of tables of the select. - * @param isJoin Indicates that the query has a join. - * @param indexRsOnTheFly The index of the result set or -1 if the query is being indexed on the fly. - * @param value The value to be indexed on the fly. - * @param operator The operand type. Used only to index on the fly. - * @param colIndex The index column. Used only to index on the fly. - * @param heap A heap to allocate temporary structures. - * @return true if the function executed correctly; false, otherwise. - */ -bool computeIndex(Context context, ResultSet** rsList, int32 size, bool isJoin, int32 indexRsOnTheFly, SQLValue* value, int32 operator, - int32 colIndex, Heap heap); -/** - * Merges two bitmaps into the first bitmap using the given boolean operator. - * - * @param bitmap1 The first bitmap. - * @param bitmap2 The second bitmap. - * @param booleanOp The boolean operator to be applied. - */ -void mergeBitmaps(IntVector* bitmap1, IntVector* bitmap2, int32 booleanOp); - -/** - * Concludes the calculation of the given aggregated function running totals based on the given record and the group count. - * - * @param record The record that is the parameter for the aggregated function. - * @param groupCount The result of a COUNT(*). - * @param aggFunctionsRunTotals The results of the aggregated functions. - * @param aggFunctionsCodes The aggregated function codes. - * @param aggFunctionsParamCols The columns that are parameters to the aggregated functions. - * @param aggFunctionsRealParamCols The real columns that are parameters to the aggregated functions. - * @param aggFunctionsColsCount The number of columns that are parameters to the aggregated functions. - * @param columnTypes The types of the columns. - * @param groupCountCols The count for the groups. - */ -void endAggFunctionsCalc(SQLValue** record, int32 groupCount, SQLValue* aggFunctionsRunTotals, int8* aggFunctionsCodes, - int32* aggFunctionsParamCols, int32* aggFunctionsRealParamCols, int32 aggFunctionsColsCount, int8* columnTypes, - int32* groupCountCols); -/** - * Creates a temporary table that stores only an integer value. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param intValue The value to be put in the table. - * @param colName The column name of the single table column. - * @return The table if the method executes correctlty; null, otherwise. - * @throws OutOfMemoryError If there is not enougth memory alloc memory. - */ -Table* createIntValueTable(Context context, TCObject driver, int32 intValue, CharP colName); - -/** - * Binds the column information of the underlying tables to the select clause. - * - * @param context The thread context where the function is being executed. - * @param clause The select clause. - * @return false if an error occurs; true, otherwise. - * @throws SQLParseException In case of an unknown or ambiguous column name, the parameter and the function data types are incompatible, or the total - * number of fields of the select exceeds the maximum. - */ -bool bindColumnsSQLSelectClause(Context context, SQLSelectClause* clause); - -/** - * Remaps a table column names, so it uses the alias names of the given field list, instead of the original names. - * - * @param context The thread context where the function is being executed. - * @param table The result set table. - * @param fieldsList The field list of the select clause. - * @param fieldsCount The number of fields of the select clause. - * @return false if an error occurs; true, otherwise. - * @throw OutOfMemoryError If a memory allocation fails. - */ -bool remapColumnsNames2Aliases(Context context, Table* table, SQLResultSetField** fieldsList, int32 fieldsCount); - -/** - * Writes the records of a result set to a table. - * - * @param context The thread context where the function is being executed. - * @param list The result set list, one for each table in the from field. - * @param numTables The number of tables of the select. - * @param rs2TableColIndexes The mapping between result set and table columns. - * @param selectClause The select clause of the query. - * @param columnIndexesTables Has the indices of the tables for each resulting column. - * @param whereClauseType Indicates the where clause is an AND or an OR. - * @param heap A heap to allocate temporary structures. - * @return The total number of records added to the table or -1 if an error occurs. - */ -int32 writeResultSetToTable(Context context, ResultSet** list, int32 numTables, Table* table, int16* rs2TableColIndexes, - SQLSelectClause* selectClause, size_t* columnIndexesTables, int32 whereClauseType, Heap heap); - -/** - * Counts the number of ON bits. - * - * @param elements The array where the bits will be counted. - * @param length The array length. - * @return The number of on bits. - */ -int32 bitCount(int32* elements, int32 length); - -/** - * Executes a join operation. - * - * @param context The thread context where the function is being executed. - * @param list The list of the result sets. - * @param numTables The number of tables of the select. - * @param table The result set table. - * @param rs2TableColIndexes The mapping between result set and table columns. - * @param values The record to be joined with. - * @param whereClauseType The type of operation used: AND or OR. - * @param heap A heap to allocate temporary structures. - * @return The number of records written to the temporary table or -1 if an error occurs. - */ -int32 performJoin(Context context, ResultSet** list, int32 numTables, Table* table, int16* rs2TableColIndexes, SQLValue** values, - int32 whereClauseType, Heap heap); - -/** - * Gets the next record to perform the join operation. - * - * @param context The thread context where the function is being executed. - * @param rsIndex The index of the result set of the list used to get the next record. - * @param verifyWhereCondition Indicates if the where clause needs to be verified. - * @param totalRs The number of result sets (tables used in the join) in the result set list. - * @param whereClauseType The type of expression in the where clause (OR or AND). - * @param rsList The list of the result sets. - * @param heap A heap to allocate temporary structures. - * @return VALIDATION_RECORD_OK, NO_RECORD, VALIDATION_RECORD_NOT_OK, - * VALIDATION_RECORD_INCOMPLETE, or -1 if an error occurs. - */ -int32 getNextRecordJoin(Context context, int32 rsIndex, bool verifyWhereCondition, int32 totalRs, int32 whereClauseType, ResultSet** rsList, - Heap heap); - -/** - * Evaluates an expression tree for a join. - * - * @param context The thread context where the function is being executed. - * @param tree The expression tree to be evaluated. - * @param rsList The list of the result sets. - * @param totalRs The number of result sets (tables used in the join) in the result set list. - * @param heap A heap to allocate temporary structures. - * @return VALIDATION_RECORD_OK, NO_RECORD, VALIDATION_RECORD_NOT_OK, - * VALIDATION_RECORD_INCOMPLETE, or -1 if an error occurs. - */ -int32 booleanTreeEvaluateJoin(Context context, SQLBooleanClauseTree* tree, ResultSet** rsList, int32 totalRs, Heap heap); - -/** - * Calculates aggregation functions. - * - * @param context The thread context where the function is being executed. - * @param record The record of the values to be used in the calculation. - * @param nullsRecord The values of the record that are null. - * @param aggFunctionsRunTotals The current totals for the aggregation functions. - * @param aggFunctionsCodes The codes of the used aggregation functions. - * @param aggFunctionsParamCols The columns that use aggregation functions. - * @param aggFunctionsColsCount The number of columns that use aggregation functions. - * @param columnTypes The types of the columns. - * @param groupCountCols The columns that use count. - */ -void performAggFunctionsCalc(Context context, SQLValue** record, uint8* nullsRecord, SQLValue* aggFunctionsRunTotals, int8* aggFunctionsCodes, - int32* aggFunctionsParamCols, int32 aggFunctionsColsCount, int8* columnTypes, int32* groupCountCols); - -/** - * Calculates the answer of a select without aggregation, join, order by, or group by without using a temporary table. - * - * @param context The thread context where the function is being executed. - * @param resultSet The result set of the table. - * @param heap A heap to allocate temporary structures. - */ -void computeAnswer(Context context, ResultSet* resultSet, Heap heap); - -/** - * Finds the best index to use in a min() or max() operation. - * - * @param field The field which may have a min() or max() operation. - */ -void findMaxMinIndex(SQLResultSetField* field); - -#endif - diff --git a/LitebaseSDK/src/native/parser/SQLUpdateStatement.c b/LitebaseSDK/src/native/parser/SQLUpdateStatement.c deleted file mode 100644 index 6d0f5ae13e..0000000000 --- a/LitebaseSDK/src/native/parser/SQLUpdateStatement.c +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Defines the functions to initialize, set, and process an update statement. - */ - -#include "SQLUpdateStatement.h" - -/** - * Constructs an update statement given the result of the parsing process. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param parser The result of the parsing process. - * @param isPrepared Indicates if the delete statement is from a prepared statement. - * @return A pointer to a SQLUpdateStatement structure or null if an error occurs. - * @throws SQLParseException If there is a field named "rowid". - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -SQLUpdateStatement* initSQLUpdateStatement(Context context, TCObject driver, LitebaseParser* parser, bool isPrepared) -{ - TRACE("sqlUpdateStatement") - Heap heap = parser->heap; - - // Creates a new update statement. - SQLUpdateStatement* updateStmt = (SQLUpdateStatement*)TC_heapAlloc(heap, sizeof(SQLUpdateStatement)); - SQLBooleanClause* whereClause = updateStmt->whereClause = parser->whereClause; - - int32 i = updateStmt->nValues = parser->fieldValuesSize; - Table* table; - JCharP value; - SQLValue* record; - CharP* fields; - - updateStmt->heap = heap; - updateStmt->type = CMD_UPDATE; - - if (isPrepared) // Some structures from the parser does not need to be reallocated when not using prepared statements. - { - updateStmt->rsTable = initSQLResultSetTable((*parser->tableList)->tableName, (*parser->tableList)->aliasTableName, heap); - fields = updateStmt->fields = (CharP*)TC_heapAlloc(heap, i * TSIZE); - xmemmove(updateStmt->fields, parser->fieldNames, i * TSIZE); - if (whereClause) - { - whereClause->fieldList = (SQLResultSetField**)TC_heapAlloc(heap, whereClause->fieldsCount * TSIZE); - xmemmove(whereClause->fieldList, parser->whereFieldList, whereClause->fieldsCount * TSIZE); - whereClause->paramList = (SQLBooleanClauseTree**)TC_heapAlloc(heap, whereClause->paramCount * TSIZE); - xmemmove(whereClause->paramList, parser->whereParamList, whereClause->paramCount * TSIZE); - } - } - else - { - updateStmt->rsTable = *parser->tableList; - fields = updateStmt->fields = parser->fieldNames; - if (whereClause) - { - whereClause->fieldList = parser->whereFieldList; - whereClause->paramList = parser->whereParamList; - } - } - - // If it is not possible to load the table, frees the structures and returns. - if (!(updateStmt->rsTable->table = table = getTable(context, driver, updateStmt->rsTable->tableName))) - return null; - - // Alocates space for the record and the nulls. - updateStmt->record = (SQLValue**)TC_heapAlloc(heap, i = table->columnCount * TSIZE); - updateStmt->storeNulls = (uint8*)TC_heapAlloc(heap, NUMBEROFBYTES(i)); - - // Allocates space for the list of the parameters. Worst case: all fields are parameters. - updateStmt->paramIndexes = (uint8*)TC_heapAlloc(heap, i); - updateStmt->paramDefined = (uint8*)TC_heapAlloc(heap, i); - - // juliana@262_1: now it is not allowed duplicated fields in an update statement. - if ((i = updateStmt->nValues) >= table->columnCount) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_DUPLICATED_COLUMN_NAME), ""); - return null; - } - - while (--i >= 0) - { - // juliana@230_40: rowid cannot be an update field. - if (TC_hashCode(fields[i]) == HCROWID) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_ROWID_CANNOT_BE_CHANGED), 0); - return null; - } - - if ((value = parser->fieldValues[i])) // Only stores values that are not null. - { - record = updateStmt->record[i] = (SQLValue*)TC_heapAlloc(heap, sizeof(SQLValue)); - record->asChars = value; - record->length = TC_JCharPLen(value); - } - else - setBit(updateStmt->storeNulls, i, true); - } - - return updateStmt; -} - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows DriverException If the parameter type is incompatible with the column type. - */ -bool setNumericParamValueUpd(Context context, SQLUpdateStatement* updateStmt, int32 index, VoidP value, int32 type) -{ - TRACE("setNumericParamValueUpd") - - if (checkUpdateIndex(context, updateStmt, index)) // Checks if the index is within the range. - { - if (index < updateStmt->paramCount) // The parameter is in the update clause. - { - int32 i; - SQLValue* record; - - // Checks if the column type is the same of the value type. - if (updateStmt->rsTable->table->columnTypes[i = updateStmt->paramIndexes[index]] != type) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES), 0); - return false; - } - - setUpdateRecord(updateStmt, index); // Sets the record in the given index. - - // Sets the values of the parameter in its list. - setBit(updateStmt->storeNulls, i, (record = updateStmt->record[i])->isNull = false); - switch (type) - { - case SHORT_TYPE: - record->asShort = *((int16*)value); - break; - case INT_TYPE: - record->asInt = *((int32*)value); - break; - case LONG_TYPE: - record->asLong = *((int64*)value); - break; - case FLOAT_TYPE : - record->asFloat = (float)*((double*)value); - break; - case DOUBLE_TYPE : - record->asDouble = *((double*)value); - } - return true; - } - else // The parameter is in the where clause. - return setNumericParamValue(context, updateStmt->whereClause->paramList[index - updateStmt->paramCount], value, type); - } - return false; -} - -/* - * Sets the value of a string or blob parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string or blob. - * @param isStr Indicates if the parameter is a string or a blob. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @thows DriverException If the parameter type is incompatible with the column type. - * @return false if an error occurs; true, otherwise. - */ -bool setStrBlobParamValueUpd(Context context, SQLUpdateStatement* updateStmt, int32 index, VoidP value, int32 length, bool isStr) -{ - TRACE("setStrBlobParamValueUpd") - int32 i; - SQLValue* record; - - if (checkUpdateIndex(context, updateStmt, index)) // Checks if the index is within the range. - { - if (index < updateStmt->paramCount) // The parameter is in the update clause. - { - // If the column is a blob, the value type must be a blob. - if ((!isStr && updateStmt->rsTable->table->columnTypes[i = updateStmt->paramIndexes[index]] != BLOB_TYPE) - || (isStr && updateStmt->rsTable->table->columnTypes[i = updateStmt->paramIndexes[index]] == BLOB_TYPE)) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_INCOMPATIBLE_TYPES), 0); - return false; - } - - setUpdateRecord(updateStmt, index); // Sets the record in the given index. - record = updateStmt->record[i]; - - if (value) // The value is not null. - { - if (isStr) - record->asChars = value; - else - record->asBlob = value; - record->length = length; - setBit(updateStmt->storeNulls, i, record->isNull = false); - } - else // The value is null. - setBit(updateStmt->storeNulls, i, record->isNull = true); - - return true; - } - else // The parameter is in the where clause. - { - if (!value) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_PARAM_NULL)); - return false; - } - else - return setParamValueString(context, updateStmt->whereClause->paramList[index - updateStmt->paramCount], value, length); - } - } - return false; -} - -// juliana@223_3: PreparedStatement.setNull() now works for blobs. -/** - * Sets null in a given field. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @param index The index of the parameter. - * @throws SQLParseException If the index is for the where clause. - * @return false if an error occurs; true, otherwise. - */ -bool setNullUpd(Context context, SQLUpdateStatement* updateStmt, int32 index) -{ - TRACE("setStrBlobParamValueUpd") - int32 i; - SQLValue* record; - - if (checkUpdateIndex(context, updateStmt, index)) // Checks if the index is within the range. - { - if (index < updateStmt->paramCount) // The parameter is in the update clause. - { - setUpdateRecord(updateStmt, index); // Sets the record in the given index. - - // The value is null. - (record = updateStmt->record[i = updateStmt->paramIndexes[index]])->asChars = null; - record->asBlob = null; - record->length = 0; - setBit(updateStmt->storeNulls, i, record->isNull = true); - - return true; - } - else // The parameter is in the where clause. - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_PARAM_NULL)); - return false; - } - } - return false; -} - -/** - * Throws an exception if the index to set a parameter in the update prepared statement is invalid. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL insert statement. - * @param index The index of the parameter. - * @throws IllegalArgumentException If the parameter index is invalid. - */ -bool checkUpdateIndex(Context context, SQLUpdateStatement* updateStmt, int32 index) -{ - TRACE("checkUpdateIndex") - - // Checks if the index is within the range. - if (index < 0 || index >= updateStmt->paramCount + (updateStmt->whereClause? updateStmt->whereClause->paramCount : 0)) - { - TC_throwExceptionNamed(context, "java.lang.IllegalStateException", getMessage(ERR_INVALID_PARAMETER_INDEX), index); - return false; - } - return true; -} - -/** - * Set a record position for an update prepared statement. - * - * @param updateStmt A SQL insert statement. - * @param index The index of the parameter. - */ -void setUpdateRecord(SQLUpdateStatement* updateStmt, int32 index) -{ - TRACE("setUpdateRecord") - int32 i = updateStmt->paramIndexes[index]; - - // It is not necessary to re-alocate a record value. - if (updateStmt->record[i]) - xmemzero(updateStmt->record[i], sizeof(SQLValue)); - else - updateStmt->record[i] = (SQLValue*)TC_heapAlloc(updateStmt->heap, sizeof(SQLValue)); - - // Sets the values of the parameter in its list. - updateStmt->paramDefined[index] = true; -} - -/** - * Clears all parameter values of a prepared statement update. - * - * @param updateStmt A SQL update statement. - */ -void clearParamValuesUpd(SQLUpdateStatement* updateStmt) -{ - TRACE("clearParamValuesUpd") - int32 i = updateStmt->paramCount, - j; - uint8* paramIndexes = updateStmt->paramIndexes; - uint8* paramDefined = updateStmt->paramDefined; - uint8* storeNulls = updateStmt->storeNulls; - SQLValue** record =updateStmt->record; - SQLBooleanClause* whereClause = updateStmt->whereClause; - while (--i >= 0) // Cleans the parameter values of the update clause. - { - xmemzero(record[j = paramIndexes[i]], sizeof(SQLValue)); - setBit(storeNulls, j, paramDefined[j] = false); - record[j]->isNull = true; - } - - if (whereClause) // Cleans the parameter values of the where clause. - { - SQLBooleanClauseTree** paramList = whereClause->paramList; - i = whereClause->paramCount; - while (--i >= 0) - paramList[i]->isParamValueDefined = false; - } -} - -/** - * Checks if all parameters values are defined. - * - * @param updateStmt A SQL update statement. - * @return true, if all parameters values are defined; false otherwise. - */ -bool allParamValuesDefinedUpd(SQLUpdateStatement* updateStmt) -{ - TRACE("allParamValuesDefinedUpd") - int32 i = updateStmt->paramCount; - uint8* paramDefined = updateStmt->paramDefined; - SQLBooleanClause* whereClause = updateStmt->whereClause; - - while (--i >= 0) // Checks if all the parameters of the update clause are defined. - if (!paramDefined[i]) - return false; - - if (whereClause) // Checks if all pararameters of the where clause are defined. - { - SQLBooleanClauseTree** paramList = whereClause->paramList; - i = whereClause->paramCount; - while (--i >= 0) - if (!paramList[i]->isParamValueDefined) - return false; - } - - return true; -} - -/** - * Executes an update statement. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @return The number of rows that were updated, or -1 if an error occurs. - * @throws OutOfMemoryError If a memory allocation fails. - * @throws DriverException If the table is not set. - */ -int32 litebaseDoUpdate(Context context, SQLUpdateStatement* updateStmt) -{ - TRACE("litebaseDoUpdate") - Table* table = updateStmt->rsTable->table; - SQLValue** record = updateStmt->record; - int32 nn; - ResultSet* rs; - Heap heap = heapCreate(); - - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - - if (!table) - { - TC_throwExceptionNamed(context, "litebase.DriverException", getMessage(ERR_CANT_READ), updateStmt->rsTable->tableName); - goto error; - } - - // juliana@250_10: removed some cases when a table was marked as not closed properly without being changed. - // juliana@226_4: now a table won't be marked as not closed properly if the application stops suddenly and the table was not modified since its - // last opening. - // Verifies if there are any parameters missing and the nulls do not violate a null restriction. - // Creates the result set that will be used to update the rows. - if (!verifyNullValues(context, table, record, CMD_UPDATE, updateStmt->nValues) - || !sqlBooleanClausePreVerify(context, updateStmt->whereClause) - || !(rs = createSimpleResultSet(context, table, updateStmt->whereClause, heap)) - || !setModified(context, table)) - goto error; - - nn = 0; - - // juliana@223_14: solved possible memory problems. - while (true) - { - IF_HEAP_ERROR(heap) - { - TC_throwExceptionNamed(context, "java.lang.OutOfMemoryError", null); - goto error; - } - if (!getNextRecord(context, rs, heap)) - break; - if (writeRecord(context, table, record, rs->pos, heap)) - nn++; - else - goto error; - } - heapDestroy(heap); - return nn; - -error: - heapDestroy(heap); - return -1; -} - -/** - * Binds an update statement. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @return true, if the statement was bound successfully; false otherwise. - * @throws SQLParseException if the number of fields is greater than 254. - */ -bool litebaseBindUpdateStatement(Context context, SQLUpdateStatement* updateStmt) -{ - TRACE("litebaseBindUpdateStatement") - int32 i = -1, - valuesCount = updateStmt->nValues, - paramCount = 0; - - Table *table = updateStmt->rsTable->table; // Gets the statement base table. - SQLValue** record = updateStmt->record; - CharP* fields = updateStmt->fields; - uint8* paramIndexes = updateStmt->paramIndexes; - uint8* storeNulls = updateStmt->storeNulls; - JCharP asChars; - - // juliana@227_17: corrected a possible crash if one tries to update more than 128 fields in a table. - if (valuesCount > MAXIMUMS) - { - TC_throwExceptionNamed(context, "litebase.SQLParseException", getMessage(ERR_MAX_NUM_FIELDS_REACHED), 0); - return false; - } - - while (++i < valuesCount) // Checks if there are undefined values. - // Identifies the values that are placeholders for parameters. - if (record[i] && (asChars = record[i]->asChars) && asChars[0] == (JChar)'?' && !asChars[1]) - paramIndexes[paramCount++] = i; - - updateStmt->paramCount = paramCount; - - // Makes sure the fields are in correct order, aligned with the table order. - if (!reorder(context, table, fields, record, storeNulls, &updateStmt->nValues, paramIndexes)) - return false; - - updateStmt->record = record; - xmemmove(storeNulls, table->storeNulls, NUMBEROFBYTES(table->columnCount)); - - // Converts the values to be updated into its correct type and binds the where clause to its table. - if (!convertStringsToValues(context, table, record, updateStmt->nValues) - || (updateStmt->whereClause - && !bindColumnsSQLBooleanClause(context, updateStmt->whereClause, &table->htName2index, table->columnTypes, &updateStmt->rsTable, 1, updateStmt->heap))) - return false; - - return true; -} diff --git a/LitebaseSDK/src/native/parser/SQLUpdateStatement.h b/LitebaseSDK/src/native/parser/SQLUpdateStatement.h deleted file mode 100644 index 74d82186c2..0000000000 --- a/LitebaseSDK/src/native/parser/SQLUpdateStatement.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2000-2013 SuperWaba Ltda. -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda. -// -// SPDX-License-Identifier: LGPL-2.1-only - -/** - * Declares the functions to initialize, set, and process an update statement. - */ - -#ifndef LITEBASE_SQLUPDATESTATEMENT_H -#define LITEBASE_SQLUPDATESTATEMENT_H - -#include "Litebase.h" - -/** - * Constructs an update statement given the result of the parsing process. - * - * @param context The thread context where the function is being executed. - * @param driver The connection with Litebase. - * @param parser The result of the parsing process. - * @param isPrepared Indicates if the delete statement is from a prepared statement. - * @return A pointer to a SQLUpdateStatement structure or null if an error occurs. - * @throws SQLParseException If there is a field named "rowid". - * @throws OutOfMemoryError If a heap memory allocation fails. - */ -SQLUpdateStatement* initSQLUpdateStatement(Context context, TCObject driver, LitebaseParser* parse, bool isPrepared); - -/* - * Sets the value of a numeric parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param type The type of the parameter. - * @return false if an error occurs; true, otherwise. - * @thows DriverException If the parameter type is incompatible with the column type. - */ -bool setNumericParamValueUpd(Context context, SQLUpdateStatement* updateStmt, int32 index, VoidP value, int32 type); - -/* - * Sets the value of a string or blob parameter at the given index. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @param index The index of the parameter. - * @param value The value of the parameter. - * @param length The length of the string or blob. - * @param isStr Indicates if the parameter is a string or a blob. - * @return false if an error occurs; true, otherwise. - * @throws SQLParserException If a null is used as a parameter of a where clause. - * @thows DriverException If the parameter type is incompatible with the column type. - */ -bool setStrBlobParamValueUpd(Context context, SQLUpdateStatement* updateStmt, int32 index, VoidP value, int32 length, bool isStr); - -// juliana@223_3: PreparedStatement.setNull() now works for blobs. -/** - * Sets null in a given field. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @param index The index of the parameter. - * @throws SQLParseException If the index is for the where clause. - * @return false if an error occurs; true, otherwise. - */ -bool setNullUpd(Context context, SQLUpdateStatement* updateStmt, int32 index); - -/** - * Throws an exception if the index to set a parameter in the update prepared statement is invalid. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL insert statement. - * @param index The index of the parameter. - * @throws IllegalArgumentException If the parameter index is invalid. - */ -bool checkUpdateIndex(Context context, SQLUpdateStatement* updateStmt, int32 index); - -/** - * Set a record position for an update prepared statement. - * - * @param updateStmt A SQL insert statement. - * @param index The index of the parameter. - */ -void setUpdateRecord(SQLUpdateStatement* updateStmt, int32 index); - -/** - * Clears all parameter values of a prepared statement update. - * - * @param updateStmt A SQL update statement. - */ -void clearParamValuesUpd(SQLUpdateStatement* updateStmt); - -/** - * Checks if all parameters values are defined. - * - * @param updateStmt A SQL update statement. - * @return true, if all parameters values are defined; false otherwise. - */ -bool allParamValuesDefinedUpd(SQLUpdateStatement* updateStmt); - -/** - * Executes an update statement. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @return The number of rows that were updated, or -1 if an error occurs. - * @throws OutOfMemoryError If a memory allocation fails. - * @throws DriverException If the table is not set. - */ -int32 litebaseDoUpdate(Context context, SQLUpdateStatement* updateStmt); - -/** - * Binds an update statement. - * - * @param context The thread context where the function is being executed. - * @param updateStmt A SQL update statement. - * @return true, if the statement was bound successfully; false otherwise. - @throws SQLParseException if the number of fields is greater than 254. - */ -bool litebaseBindUpdateStatement(Context context, SQLUpdateStatement* updateStmt); - -#endif diff --git a/TotalCrossSDK/build.gradle b/TotalCrossSDK/build.gradle index 41b90e0544..6cdbcabc74 100644 --- a/TotalCrossSDK/build.gradle +++ b/TotalCrossSDK/build.gradle @@ -14,7 +14,7 @@ apply plugin: 'java' apply plugin: 'maven-publish' sourceCompatibility = 1.8 -version = '7.1.0' +version = '7.2.0' group = 'com.totalcross' archivesBaseName = 'totalcross-sdk' @@ -46,13 +46,6 @@ sourceSets { java { } } - litebase { - java { - srcDirs = [new File(project.rootDir, '../LitebaseSDK/src/java')] - exclude 'samples/**', 'litebase/android/**', '**/*4D.java' - } - compileClasspath += sourceSets.main.runtimeClasspath - } } buildscript { @@ -83,7 +76,7 @@ dependencies { // https://mvnrepository.com/artifact/com.googlecode.plist/dd-plist compile group: 'com.googlecode.plist', name: 'dd-plist', version: '1.19' // https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc - compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.8.7' + compile group: 'org.xerial', name: 'sqlite-jdbc', version: '3.32.3.3' // https://mvnrepository.com/artifact/de.schlichtherle.truezip/truezip-file compile group: 'de.schlichtherle.truezip', name: 'truezip-file', version: '7.5.1' // https://mvnrepository.com/artifact/de.schlichtherle.truezip/truezip-driver-zip @@ -104,7 +97,7 @@ dependencies { runtime group: 'net.java.dev.jna', name: 'jna-platform', version: '4.2.2' // https://mvnrepository.com/artifact/net.coobird/thumbnailator - compile group: 'net.coobird', name: 'thumbnailator', version: '0.4.8' + compile group: 'net.coobird', name: 'thumbnailator', version: '0.4.20' // https://mvnrepository.com/artifact/junit/junit testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' @@ -122,7 +115,6 @@ test { jar { exclude 'totalcross/res/mp3/**', 'totalcross/chartypes.bin' - from(sourceSets.litebase.output) from new File(project.rootDir, 'etc/fonts/TCFont.tcz') from new File(project.rootDir, 'etc/fonts/Material Icons.tcz') into("META-INF/maven/$project.group/$baseName") { @@ -132,9 +124,9 @@ jar { rename ".*", "pom.xml" } - // collect all dependencies + // collect all runtime dependencies shipped in dist/libs int i = 0; - String classpathVar = configurations.compile.collect { " libs/" + (i++==0?String.format("%0\$-50s", it.getName()):String.format("%0\$-62s", it.getName())) }.join(" "); + String classpathVar = configurations.runtimeClasspath.collect { " libs/" + (i++==0?String.format("%0\$-50s", it.getName()):String.format("%0\$-62s", it.getName())) }.join(" "); manifest { attributes('Main-Class': 'totalcross.Launcher') manifest { @@ -257,7 +249,12 @@ task deployTcui(type:JavaExec, dependsOn: [jar, tcuiJar]) { configurations.runtime.collect()) } -task dist(dependsOn: build) { +task copySdkDependencies(type: Copy) { + from configurations.runtimeClasspath + into 'dist/libs' +} + +task dist(dependsOn: [build, copySdkDependencies]) { doLast{ copy { @@ -287,7 +284,6 @@ task sourcesJar(type: Jar) { from (sourceSets.main.allSource) { exclude 'tc/**', '**/*4A.java', '**/*4D.java' } - from (sourceSets.litebase.allSource) } javadoc.finalizedBy(sourcesJar) diff --git a/TotalCrossSDK/build.xml b/TotalCrossSDK/build.xml index 01c0b9cb07..8c00a55e32 100644 --- a/TotalCrossSDK/build.xml +++ b/TotalCrossSDK/build.xml @@ -31,7 +31,6 @@ - @@ -183,12 +182,6 @@ target: javadoc - - - - - - @@ -222,7 +215,7 @@ target: javadoc - + diff --git a/TotalCrossSDK/docs/TotalCross Companion.lyx b/TotalCrossSDK/docs/TotalCross Companion.lyx index 07822ed8e6..d625610b59 100644 --- a/TotalCrossSDK/docs/TotalCross Companion.lyx +++ b/TotalCrossSDK/docs/TotalCross Companion.lyx @@ -535,7 +535,7 @@ Linux 32 bits (we only assure that it works on Debian distribution). \begin_layout Itemize Browser as Java applet (JDK 1.1 and above). Note that in this case you must enable some permission so that you can - use files and, consequently, Litebase. + use files. \end_layout \begin_layout Itemize @@ -791,7 +791,7 @@ In the sample above, commenting out the line marked with *****, the log \begin_layout Standard Generally speaking, you can create a thread to listen to a socket or a file - or even a Litebase table in background, but be aware that if you try to + in background, but be aware that if you try to access the same resource by different threads your application might just blow up. We also don't recommend running the user interface in a background thread, @@ -1213,7 +1213,6 @@ totalcross.* TCFont.tcz \family default is needed to run an application on Java. - It also contains Litebase classes. \end_layout \begin_layout Itemize @@ -1237,7 +1236,6 @@ tc.jar TCFont.tcz. \family default - It also contains Litebase classes. \end_layout \begin_layout Itemize @@ -1246,9 +1244,6 @@ TCFont.tcz. samples \family default : Installation files for all samples. - Litebase samples are also inside, in the folder -\family typewriter -litebase \family default . Lets take the TotalCrossAPI sample, located at @@ -1287,7 +1282,7 @@ applet \family default , otherwise errors may occur. You should also give special permission for file usage so that it is possible - to manipulate files and Litebase. + to manipulate files. \end_layout \begin_layout Itemize @@ -1363,7 +1358,7 @@ wp8 \family typewriter .xap \family default - file which includes the sample, TotalCross, and Litebase. + file which includes the sample. Note that to install the sample you must enable your phone to allow development mode. \end_layout @@ -1436,7 +1431,7 @@ iOS vm \family default : virtual machine files that which will interpret your TotalCross code on - the device and Litebase library. + the device. Below are instructions of how to install them in each platform: \end_layout @@ -1454,12 +1449,8 @@ TCFont.tcz \family typewriter TCUI.tcz \family default -, and -\family typewriter -LitebaseLib.tcz -\family default . - These files are necessary to run TotalCross and Litebase on Windows 32 + These files are necessary to run TotalCross on Windows 32 and Linux. \end_layout @@ -1477,7 +1468,6 @@ TotalCross.ipa .ipa \family default file for your application and the samples if you have an Apple key. - It already includes Litebase. \end_layout \begin_layout Itemize @@ -1488,10 +1478,6 @@ win32 : have the files \family typewriter TCVM.dll -\family default - and -\family typewriter -Litebase.dll \family default on the same folder of the application or install TotalCross properly in your operational system. @@ -1511,7 +1497,6 @@ TotalCross.xap .xap \family default of your application. - It already includes Litebase. \end_layout \begin_layout Itemize @@ -1527,7 +1512,7 @@ TotalCross.apk \family typewriter adb install -r TotalCross.apk \family default -), which already includes Litebase. +). \end_layout \begin_layout Itemize @@ -1539,7 +1524,7 @@ linux \family typewriter .deb \family default -) for TotalCross and Litebase to be used on linuxes. +) for TotalCross to be used on linuxes. \end_layout \end_deeper @@ -1561,9 +1546,6 @@ TotalCross Companion.pdf \begin_layout Itemize -\family typewriter -Litebase Companion.pdf -\family default . \end_layout @@ -1572,7 +1554,7 @@ The folder \family typewriter html \family default -, which contains the generated javadocs for TotalCross and Litebase. +, which contains the generated javadocs for TotalCross. Open the files \family typewriter index.html @@ -1656,14 +1638,6 @@ tc/samples: \begin_layout Itemize -\family typewriter -lb/samples: -\family default - the sources files of all Litebase samples. -\end_layout - -\begin_layout Itemize - \family typewriter totalcross: \family default @@ -2775,7 +2749,6 @@ to \family default : sets where the PDB and media files are stored. - This is also the default path for Litebase table files. \end_layout \begin_layout Itemize @@ -3477,7 +3450,7 @@ path type \family default - : Packages the VM (and Litebase) with the application, creating a single + : Packages the VM with the application, creating a single installation file. The type parameter can be one of the following: \family typewriter @@ -3485,7 +3458,7 @@ demo \family default , \family typewriter -demo+ litebase +demo \family default , \family typewriter @@ -3493,7 +3466,7 @@ release \family default , \family typewriter -release+litebase +release \family default (where demo/release are the virtual machine types you want to include: the time-limited demonstration, or the release that requires activation). @@ -5193,7 +5166,7 @@ When you generate a \family typewriter .xap \family default - of your application, which includes TotalCross and Litebase, you can install + of your application, which includes TotalCross, you can install it yourself if you enable development mode on your device. Follow the instructions in \begin_inset Newline newline @@ -5421,7 +5394,7 @@ icon72x72.png appicon.gif \family default (already used in other platforms) to 72x72 and use it. - The first time that TotalCross, Litebase, and your application run, it + The first time that TotalCross, and your application run, it will take longer because the \family typewriter .tcz @@ -5555,7 +5528,7 @@ adb install -r TotalCrossAPI.apk \end_layout \begin_layout Standard -Remember to install the TotalCross and Litebase +Remember to install the TotalCross \family typewriter .apk \family default @@ -5838,7 +5811,7 @@ Using the Release Vms \begin_layout Standard When you decide to release the application to your client, you must purchase - TotalCross+Litebase licenses. + TotalCross licenses. See the correct approach going to the TotalCross \begin_inset CommandInset href LatexCommand href @@ -7302,7 +7275,7 @@ Now it is only needed to download and install one \family typewriter .exe \family default - for TotalCross and Litebase SDK and VM. + for TotalCross SDK and VM. Make sure to change the paths used in your deploying process to reflect this change. \end_layout @@ -7369,11 +7342,6 @@ Now there is another database option in TotalCross. SQLite version for all supported platforms. If it used the one available on the device when it has one, there could be differences in the API or table format. - But remember that Litebase eases the task of synchronizing the device tables - with tables on server databases such as SQLServer or Oracle by using the - class -\family typewriter -RowIterator \family default . If you want to do something similar using SQLite, you will have to do it @@ -8079,7 +8047,6 @@ totalcross.db.sqlite . In the future, other databases might be added to TotalCross using JDBC specification. - It is also intended to provide Litebase access using the JDBC API. \end_layout \begin_layout Chapter diff --git a/TotalCrossSDK/etc/security/android_keystore.properties b/TotalCrossSDK/etc/security/android_keystore.properties new file mode 100644 index 0000000000..c3f4e10b23 --- /dev/null +++ b/TotalCrossSDK/etc/security/android_keystore.properties @@ -0,0 +1,15 @@ +# AAB Signing configuration +aab_signing_enabled=true +aab_keystore_path=etc/security/tc_aab_key.keystore +aab_keystore_digestalg=SHA-256 +aab_keystore_sigalg=SHA256withRSA +aab_keystore_storetype=JKS +aab_keystore_storepass=@ndroid$w +aab_keystore_keypass=@ndroidsw +aab_keystore_alias=tcandroidkey +# APK Signing configuration +apk_signing_enabled=true +apk_keystore_path=etc/security/tcandroidkey.keystore +apk_keystore_storepass=@ndroid$w +apk_keystore_keypass=@ndroidsw +apk_keystore_alias=tcandroidkey \ No newline at end of file diff --git a/TotalCrossSDK/etc/security/tc_aab_key.keystore b/TotalCrossSDK/etc/security/tc_aab_key.keystore new file mode 100644 index 0000000000..62a01278ca Binary files /dev/null and b/TotalCrossSDK/etc/security/tc_aab_key.keystore differ diff --git a/TotalCrossSDK/etc/security/tcandroidkey.keystore b/TotalCrossSDK/etc/security/tcandroidkey.keystore new file mode 100644 index 0000000000..21742feb81 Binary files /dev/null and b/TotalCrossSDK/etc/security/tcandroidkey.keystore differ diff --git a/TotalCrossSDK/etc/tools/android/tools/Configuration.proto b/TotalCrossSDK/etc/tools/android/tools/Configuration.proto new file mode 100755 index 0000000000..8a4644c9a2 --- /dev/null +++ b/TotalCrossSDK/etc/tools/android/tools/Configuration.proto @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +package aapt.pb; + +option java_package = "com.android.aapt"; + +// A description of the requirements a device must have in order for a +// resource to be matched and selected. +message Configuration { + enum LayoutDirection { + LAYOUT_DIRECTION_UNSET = 0; + LAYOUT_DIRECTION_LTR = 1; + LAYOUT_DIRECTION_RTL = 2; + } + + enum ScreenLayoutSize { + SCREEN_LAYOUT_SIZE_UNSET = 0; + SCREEN_LAYOUT_SIZE_SMALL = 1; + SCREEN_LAYOUT_SIZE_NORMAL = 2; + SCREEN_LAYOUT_SIZE_LARGE = 3; + SCREEN_LAYOUT_SIZE_XLARGE = 4; + } + + enum ScreenLayoutLong { + SCREEN_LAYOUT_LONG_UNSET = 0; + SCREEN_LAYOUT_LONG_LONG = 1; + SCREEN_LAYOUT_LONG_NOTLONG = 2; + } + + enum ScreenRound { + SCREEN_ROUND_UNSET = 0; + SCREEN_ROUND_ROUND = 1; + SCREEN_ROUND_NOTROUND = 2; + } + + enum WideColorGamut { + WIDE_COLOR_GAMUT_UNSET = 0; + WIDE_COLOR_GAMUT_WIDECG = 1; + WIDE_COLOR_GAMUT_NOWIDECG = 2; + } + + enum Hdr { + HDR_UNSET = 0; + HDR_HIGHDR = 1; + HDR_LOWDR = 2; + } + + enum Orientation { + ORIENTATION_UNSET = 0; + ORIENTATION_PORT = 1; + ORIENTATION_LAND = 2; + ORIENTATION_SQUARE = 3; + } + + enum UiModeType { + UI_MODE_TYPE_UNSET = 0; + UI_MODE_TYPE_NORMAL = 1; + UI_MODE_TYPE_DESK = 2; + UI_MODE_TYPE_CAR = 3; + UI_MODE_TYPE_TELEVISION = 4; + UI_MODE_TYPE_APPLIANCE = 5; + UI_MODE_TYPE_WATCH = 6; + UI_MODE_TYPE_VRHEADSET = 7; + } + + enum UiModeNight { + UI_MODE_NIGHT_UNSET = 0; + UI_MODE_NIGHT_NIGHT = 1; + UI_MODE_NIGHT_NOTNIGHT = 2; + } + + enum Touchscreen { + TOUCHSCREEN_UNSET = 0; + TOUCHSCREEN_NOTOUCH = 1; + TOUCHSCREEN_STYLUS = 2; + TOUCHSCREEN_FINGER = 3; + } + + enum KeysHidden { + KEYS_HIDDEN_UNSET = 0; + KEYS_HIDDEN_KEYSEXPOSED = 1; + KEYS_HIDDEN_KEYSHIDDEN = 2; + KEYS_HIDDEN_KEYSSOFT = 3; + } + + enum Keyboard { + KEYBOARD_UNSET = 0; + KEYBOARD_NOKEYS = 1; + KEYBOARD_QWERTY = 2; + KEYBOARD_TWELVEKEY = 3; + } + + enum NavHidden { + NAV_HIDDEN_UNSET = 0; + NAV_HIDDEN_NAVEXPOSED = 1; + NAV_HIDDEN_NAVHIDDEN = 2; + } + + enum Navigation { + NAVIGATION_UNSET = 0; + NAVIGATION_NONAV = 1; + NAVIGATION_DPAD = 2; + NAVIGATION_TRACKBALL = 3; + NAVIGATION_WHEEL = 4; + } + + // + // Axis/dimensions that are understood by the runtime. + // + + // Mobile country code. + uint32 mcc = 1; + + // Mobile network code. + uint32 mnc = 2; + + // BCP-47 locale tag. + string locale = 3; + + // Left-to-right, right-to-left... + LayoutDirection layout_direction = 4; + + // Screen width in pixels. Prefer screen_width_dp. + uint32 screen_width = 5; + + // Screen height in pixels. Prefer screen_height_dp. + uint32 screen_height = 6; + + // Screen width in density independent pixels (dp). + uint32 screen_width_dp = 7; + + // Screen height in density independent pixels (dp). + uint32 screen_height_dp = 8; + + // The smallest screen dimension, regardless of orientation, in dp. + uint32 smallest_screen_width_dp = 9; + + // Whether the device screen is classified as small, normal, large, xlarge. + ScreenLayoutSize screen_layout_size = 10; + + // Whether the device screen is long. + ScreenLayoutLong screen_layout_long = 11; + + // Whether the screen is round (Android Wear). + ScreenRound screen_round = 12; + + // Whether the screen supports wide color gamut. + WideColorGamut wide_color_gamut = 13; + + // Whether the screen has high dynamic range. + Hdr hdr = 14; + + // Which orientation the device is in (portrait, landscape). + Orientation orientation = 15; + + // Which type of UI mode the device is in (television, car, etc.). + UiModeType ui_mode_type = 16; + + // Whether the device is in night mode. + UiModeNight ui_mode_night = 17; + + // The device's screen density in dots-per-inch (dpi). + uint32 density = 18; + + // Whether a touchscreen exists, supports a stylus, or finger. + Touchscreen touchscreen = 19; + + // Whether the keyboard hardware keys are currently hidden, exposed, or + // if the keyboard is a software keyboard. + KeysHidden keys_hidden = 20; + + // The type of keyboard present (none, QWERTY, 12-key). + Keyboard keyboard = 21; + + // Whether the navigation is exposed or hidden. + NavHidden nav_hidden = 22; + + // The type of navigation present on the device + // (trackball, wheel, dpad, etc.). + Navigation navigation = 23; + + // The minimum SDK version of the device. + uint32 sdk_version = 24; + + // + // Build-time only dimensions. + // + + string product = 25; +} diff --git a/TotalCrossSDK/etc/tools/android/tools/Resources.proto b/TotalCrossSDK/etc/tools/android/tools/Resources.proto new file mode 100755 index 0000000000..be363bc102 --- /dev/null +++ b/TotalCrossSDK/etc/tools/android/tools/Resources.proto @@ -0,0 +1,639 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +import "Configuration.proto"; + +package aapt.pb; + +option java_package = "com.android.aapt"; + +// A string pool that wraps the binary form of the C++ class android::ResStringPool. +message StringPool { + bytes data = 1; +} + +// The position of a declared entity within a file. +message SourcePosition { + uint32 line_number = 1; + uint32 column_number = 2; +} + +// Developer friendly source file information for an entity in the resource table. +message Source { + // The index of the string path within the source string pool of a ResourceTable. + uint32 path_idx = 1; + SourcePosition position = 2; +} + +// The name and version fingerprint of a build tool. +message ToolFingerprint { + string tool = 1; + string version = 2; +} + +// Top level message representing a resource table. +message ResourceTable { + // The string pool containing source paths referenced throughout the resource table. This does + // not end up in the final binary ARSC file. + StringPool source_pool = 1; + + // Resource definitions corresponding to an Android package. + repeated Package package = 2; + + // The declarations within the resource table. + repeated Overlayable overlayable = 3; + + // The version fingerprints of the tools that built the resource table. + repeated ToolFingerprint tool_fingerprint = 4; +} + +// A package ID in the range [0x00, 0xff]. +message PackageId { + uint32 id = 1; +} + +// Defines resources for an Android package. +message Package { + // The package ID of this package, in the range [0x00, 0xff]. + // - ID 0x00 is reserved for shared libraries, or when the ID is assigned at run-time. + // - ID 0x01 is reserved for the 'android' package (framework). + // - ID range [0x02, 0x7f) is reserved for auto-assignment to shared libraries at run-time. + // - ID 0x7f is reserved for the application package. + // - IDs > 0x7f are reserved for the application as well and are treated as feature splits. + // This may not be set if no ID was assigned. + PackageId package_id = 1; + + // The Java compatible Android package name of the app. + string package_name = 2; + + // The series of types defined by the package. + repeated Type type = 3; +} + +// A type ID in the range [0x01, 0xff]. +message TypeId { + uint32 id = 1; +} + +// A set of resources grouped under a common type. Such types include string, layout, xml, dimen, +// attr, etc. This maps to the second part of a resource identifier in Java (R.type.entry). +message Type { + // The ID of the type. This may not be set if no ID was assigned. + TypeId type_id = 1; + + // The name of the type. This corresponds to the 'type' part of a full resource name of the form + // package:type/entry. The set of legal type names is listed in Resource.cpp. + string name = 2; + + // The entries defined for this type. + repeated Entry entry = 3; +} + +// The Visibility of a symbol/entry (public, private, undefined). +message Visibility { + // The visibility of the resource outside of its package. + enum Level { + // No visibility was explicitly specified. This is typically treated as private. + // The distinction is important when two separate R.java files are generated: a public and + // private one. An unknown visibility, in this case, would cause the resource to be omitted + // from either R.java. + UNKNOWN = 0; + + // A resource was explicitly marked as private. This means the resource can not be accessed + // outside of its package unless the @*package:type/entry notation is used (the asterisk being + // the private accessor). If two R.java files are generated (private + public), the resource + // will only be emitted to the private R.java file. + PRIVATE = 1; + + // A resource was explicitly marked as public. This means the resource can be accessed + // from any package, and is emitted into all R.java files, public and private. + PUBLIC = 2; + } + + Level level = 1; + + // The path at which this entry's visibility was defined (eg. public.xml). + Source source = 2; + + // The comment associated with the tag. + string comment = 3; + + // Indicates that the resource id may change across builds and that the public R.java identifier + // for this resource should not be final. This is set to `true` for resources in `staging-group` + // tags. + bool staged_api = 4; +} + +// Whether a resource comes from a compile-time overlay and is explicitly allowed to not overlay an +// existing resource. +message AllowNew { + // Where this was defined in source. + Source source = 1; + + // Any comment associated with the declaration. + string comment = 2; +} + +// Represents a set of overlayable resources. +message Overlayable { + // The name of the . + string name = 1; + + // The location of the declaration in the source. + Source source = 2; + + // The component responsible for enabling and disabling overlays targeting this . + string actor = 3; +} + +// Represents an overlayable declaration within an tag. +message OverlayableItem { + enum Policy { + NONE = 0; + PUBLIC = 1; + SYSTEM = 2; + VENDOR = 3; + PRODUCT = 4; + SIGNATURE = 5; + ODM = 6; + OEM = 7; + ACTOR = 8; + CONFIG_SIGNATURE = 9; + } + + // The location of the declaration in source. + Source source = 1; + + // Any comment associated with the declaration. + string comment = 2; + + // The policy defined by the enclosing tag of this . + repeated Policy policy = 3; + + // The index into overlayable list that points to the tag that contains + // this . + uint32 overlayable_idx = 4; +} + +// The staged resource ID definition of a finalized resource. +message StagedId { + Source source = 1; + uint32 staged_id = 2; +} + +// An entry ID in the range [0x0000, 0xffff]. +message EntryId { + uint32 id = 1; +} + +// An entry declaration. An entry has a full resource ID that is the combination of package ID, +// type ID, and its own entry ID. An entry on its own has no value, but values are defined for +// various configurations/variants. +message Entry { + // The ID of this entry. Together with the package ID and type ID, this forms a full resource ID + // of the form 0xPPTTEEEE, where PP is the package ID, TT is the type ID, and EEEE is the entry + // ID. + // This may not be set if no ID was assigned. + EntryId entry_id = 1; + + // The name of this entry. This corresponds to the 'entry' part of a full resource name of the + // form package:type/entry. + string name = 2; + + // The visibility of this entry (public, private, undefined). + Visibility visibility = 3; + + // Whether this resource, when originating from a compile-time overlay, is allowed to NOT overlay + // any existing resources. + AllowNew allow_new = 4; + + // Whether this resource can be overlaid by a runtime resource overlay (RRO). + OverlayableItem overlayable_item = 5; + + // The set of values defined for this entry, each corresponding to a different + // configuration/variant. + repeated ConfigValue config_value = 6; + + // The staged resource ID of this finalized resource. + StagedId staged_id = 7; +} + +// A Configuration/Value pair. +message ConfigValue { + Configuration config = 1; + Value value = 2; +} + +// The generic meta-data for every value in a resource table. +message Value { + // Where the value was defined. + Source source = 1; + + // Any comment associated with the value. + string comment = 2; + + // Whether the value can be overridden. + bool weak = 3; + + // The value is either an Item or a CompoundValue. + oneof value { + Item item = 4; + CompoundValue compound_value = 5; + } +} + +// An Item is an abstract type. It represents a value that can appear inline in many places, such +// as XML attribute values or on the right hand side of style attribute definitions. The concrete +// type is one of the types below. Only one can be set. +message Item { + oneof value { + Reference ref = 1; + String str = 2; + RawString raw_str = 3; + StyledString styled_str = 4; + FileReference file = 5; + Id id = 6; + Primitive prim = 7; + } +} + +// A CompoundValue is an abstract type. It represents a value that is a made of other values. +// These can only usually appear as top-level resources. The concrete type is one of the types +// below. Only one can be set. +message CompoundValue { + oneof value { + Attribute attr = 1; + Style style = 2; + Styleable styleable = 3; + Array array = 4; + Plural plural = 5; + MacroBody macro = 6; + } +} + +// Message holding a boolean, so it can be optionally encoded. +message Boolean { + bool value = 1; +} + +// A value that is a reference to another resource. This reference can be by name or resource ID. +message Reference { + enum Type { + // A plain reference (@package:type/entry). + REFERENCE = 0; + + // A reference to a theme attribute (?package:type/entry). + ATTRIBUTE = 1; + } + + Type type = 1; + + // The resource ID (0xPPTTEEEE) of the resource being referred. This is optional. + uint32 id = 2; + + // The name of the resource being referred. This is optional if the resource ID is set. + string name = 3; + + // Whether this reference is referencing a private resource (@*package:type/entry). + bool private = 4; + + // Whether this reference is dynamic. + Boolean is_dynamic = 5; + + // The type flags used when compiling the reference. Used for substituting the contents of macros. + uint32 type_flags = 6; + + // Whether raw string values would have been accepted in place of this reference definition. Used + // for substituting the contents of macros. + bool allow_raw = 7; +} + +// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a +// resource ID (0xPPTTEEEE) as a unique identifier. Their value is unimportant. +message Id { +} + +// A value that is a string. +message String { + string value = 1; +} + +// A value that is a raw string, which is unescaped/uninterpreted. This is typically used to +// represent the value of a style attribute before the attribute is compiled and the set of +// allowed values is known. +message RawString { + string value = 1; +} + +// A string with styling information, like html tags that specify boldness, italics, etc. +message StyledString { + // The raw text of the string. + string value = 1; + + // A Span marks a region of the string text that is styled. + message Span { + // The name of the tag, and its attributes, encoded as follows: + // tag_name;attr1=value1;attr2=value2;[...] + string tag = 1; + + // The first character position this span applies to, in UTF-16 offset. + uint32 first_char = 2; + + // The last character position this span applies to, in UTF-16 offset. + uint32 last_char = 3; + } + + repeated Span span = 2; +} + +// A value that is a reference to an external entity, like an XML file or a PNG. +message FileReference { + enum Type { + UNKNOWN = 0; + PNG = 1; + BINARY_XML = 2; + PROTO_XML = 3; + } + + // Path to a file within the APK (typically res/type-config/entry.ext). + string path = 1; + + // The type of file this path points to. For UAM bundle, this cannot be + // BINARY_XML. + Type type = 2; +} + +// A value that represents a primitive data type (float, int, boolean, etc.). +// Refer to Res_value in ResourceTypes.h for info on types and formatting +message Primitive { + message NullType { + } + message EmptyType { + } + oneof oneof_value { + NullType null_value = 1; + EmptyType empty_value = 2; + float float_value = 3; + uint32 dimension_value = 13; + uint32 fraction_value = 14; + int32 int_decimal_value = 6; + uint32 int_hexadecimal_value = 7; + bool boolean_value = 8; + uint32 color_argb8_value = 9; + uint32 color_rgb8_value = 10; + uint32 color_argb4_value = 11; + uint32 color_rgb4_value = 12; + float dimension_value_deprecated = 4 [deprecated=true]; + float fraction_value_deprecated = 5 [deprecated=true]; + } +} + +// A value that represents an XML attribute and what values it accepts. +message Attribute { + // A Symbol used to represent an enum or a flag. + message Symbol { + // Where the enum/flag item was defined. + Source source = 1; + + // Any comments associated with the enum or flag. + string comment = 2; + + // The name of the enum/flag as a reference. Enums/flag items are generated as ID resource + // values. + Reference name = 3; + + // The value of the enum/flag. + uint32 value = 4; + + // The data type of the enum/flag as defined in android::Res_value. + uint32 type = 5; + } + + // Bitmask of formats allowed for an attribute. + enum FormatFlags { + NONE = 0x0; // Proto3 requires a default of 0. + ANY = 0x0000ffff; // Allows any type except ENUM and FLAGS. + REFERENCE = 0x01; // Allows Reference values. + STRING = 0x02; // Allows String/StyledString values. + INTEGER = 0x04; // Allows any integer BinaryPrimitive values. + BOOLEAN = 0x08; // Allows any boolean BinaryPrimitive values. + COLOR = 0x010; // Allows any color BinaryPrimitive values. + FLOAT = 0x020; // Allows any float BinaryPrimitive values. + DIMENSION = 0x040; // Allows any dimension BinaryPrimitive values. + FRACTION = 0x080; // Allows any fraction BinaryPrimitive values. + ENUM = 0x00010000; // Allows enums that are defined in the Attribute's symbols. + // ENUM and FLAGS cannot BOTH be set. + FLAGS = 0x00020000; // Allows flags that are defined in the Attribute's symbols. + // ENUM and FLAGS cannot BOTH be set. + } + + // A bitmask of types that this XML attribute accepts. Corresponds to the flags in the + // enum FormatFlags. + uint32 format_flags = 1; + + // The smallest integer allowed for this XML attribute. Only makes sense if the format includes + // FormatFlags::INTEGER. + int32 min_int = 2; + + // The largest integer allowed for this XML attribute. Only makes sense if the format includes + // FormatFlags::INTEGER. + int32 max_int = 3; + + // The set of enums/flags defined in this attribute. Only makes sense if the format includes + // either FormatFlags::ENUM or FormatFlags::FLAGS. Having both is an error. + repeated Symbol symbol = 4; +} + +// A value that represents a style. +message Style { + // An XML attribute/value pair defined in the style. + message Entry { + // Where the entry was defined. + Source source = 1; + + // Any comments associated with the entry. + string comment = 2; + + // A reference to the XML attribute. + Reference key = 3; + + // The Item defined for this XML attribute. + Item item = 4; + } + + // The optinal style from which this style inherits attributes. + Reference parent = 1; + + // The source file information of the parent inheritance declaration. + Source parent_source = 2; + + // The set of XML attribute/value pairs for this style. + repeated Entry entry = 3; +} + +// A value that represents a XML resource. These are not real resources and +// only end up as Java fields in the generated R.java. They do not end up in the binary ARSC file. +message Styleable { + // An attribute defined for this styleable. + message Entry { + // Where the attribute was defined within the block. + Source source = 1; + + // Any comments associated with the declaration. + string comment = 2; + + // The reference to the attribute. + Reference attr = 3; + } + + // The set of attribute declarations. + repeated Entry entry = 1; +} + +// A value that represents an array of resource values. +message Array { + // A single element of the array. + message Element { + // Where the element was defined. + Source source = 1; + + // Any comments associated with the element. + string comment = 2; + + // The value assigned to this element. + Item item = 3; + } + + // The list of array elements. + repeated Element element = 1; +} + +// A value that represents a string and its many variations based on plurality. +message Plural { + // The arity of the plural. + enum Arity { + ZERO = 0; + ONE = 1; + TWO = 2; + FEW = 3; + MANY = 4; + OTHER = 5; + } + + // The plural value for a given arity. + message Entry { + // Where the plural was defined. + Source source = 1; + + // Any comments associated with the plural. + string comment = 2; + + // The arity of the plural. + Arity arity = 3; + + // The value assigned to this plural. + Item item = 4; + } + + // The set of arity/plural mappings. + repeated Entry entry = 1; +} + +// Defines an abstract XmlNode that must be either an XmlElement, or +// a text node represented by a string. +message XmlNode { + oneof node { + XmlElement element = 1; + string text = 2; + } + + // Source line and column info. + SourcePosition source = 3; +} + +// An in an XML document. +message XmlElement { + // Namespaces defined on this element. + repeated XmlNamespace namespace_declaration = 1; + + // The namespace URI of this element. + string namespace_uri = 2; + + // The name of this element. + string name = 3; + + // The attributes of this element. + repeated XmlAttribute attribute = 4; + + // The children of this element. + repeated XmlNode child = 5; +} + +// A namespace declaration on an XmlElement (xmlns:android="http://..."). +message XmlNamespace { + string prefix = 1; + string uri = 2; + + // Source line and column info. + SourcePosition source = 3; +} + +// An attribute defined on an XmlElement (android:text="..."). +message XmlAttribute { + string namespace_uri = 1; + string name = 2; + string value = 3; + + // Source line and column info. + SourcePosition source = 4; + + // The optional resource ID (0xPPTTEEEE) of the attribute. + uint32 resource_id = 5; + + // The optional interpreted/compiled version of the `value` string. + Item compiled_item = 6; +} + +message MacroBody { + string raw_string = 1; + StyleString style_string = 2; + repeated UntranslatableSection untranslatable_sections = 3; + repeated NamespaceAlias namespace_stack = 4; + SourcePosition source = 5; +} + +message NamespaceAlias { + string prefix = 1; + string package_name = 2; + bool is_private = 3; +} + +message StyleString { + message Span { + string name = 1; + uint32 start_index = 2; + uint32 end_index = 3; + } + string str = 1; + repeated Span spans = 2; +} + +message UntranslatableSection { + uint64 start_index = 1; + uint64 end_index = 2; +} \ No newline at end of file diff --git a/TotalCrossSDK/etc/tools/iOSCodesign/xcassetsGenerator.sh b/TotalCrossSDK/etc/tools/iOSCodesign/xcassetsGenerator.sh index d0d1b60a82..2e12ce2e33 100644 --- a/TotalCrossSDK/etc/tools/iOSCodesign/xcassetsGenerator.sh +++ b/TotalCrossSDK/etc/tools/iOSCodesign/xcassetsGenerator.sh @@ -24,7 +24,7 @@ BASEDIR=$2 # http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script command -v convert >/dev/null 2>&1 || { echo >&2 "I require imagemagick but it's not installed. If you have brew installed, execute 'brew install imagemagick' Aborting."; exit 1; } -iconPath= "${BASEDIR}/Assets.xcassets/AppIcon.appiconset" +iconPath="${BASEDIR}/Assets.xcassets/AppIcon.appiconset" mkdir -p "$iconPath" diff --git a/TotalCrossSDK/etc/tools/ipa/AppleRootCA.pem b/TotalCrossSDK/etc/tools/ipa/AppleRootCA.pem new file mode 100644 index 0000000000..a27252d0a1 --- /dev/null +++ b/TotalCrossSDK/etc/tools/ipa/AppleRootCA.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET +MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0 +MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw +bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+ ++FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1 +XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w +tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW +q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM +aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3 +R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE +ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93 +d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl +IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0 +YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj +b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp +Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc +NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP +y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7 +R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg +xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP +IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX +UKqK1drk/NAJBzewdXUh +-----END CERTIFICATE----- diff --git a/TotalCrossSDK/etc/tools/ipa/AppleWWDRCA.pem b/TotalCrossSDK/etc/tools/ipa/AppleWWDRCA.pem new file mode 100644 index 0000000000..b3e6b454c8 --- /dev/null +++ b/TotalCrossSDK/etc/tools/ipa/AppleWWDRCA.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEIzCCAwugAwIBAgIBGTANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET +MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDgwMjE0MTg1 +NjM1WhcNMTYwMjE0MTg1NjM1WjCBljELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFw +cGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVs +YXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0 +aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMo4VKbLVqrIJDlI6Yzu7F+4fyaRvDRTes58Y4Bhd2RepQcj +tjn+UC0VVlhwLX7EbsFKhT4v8N6EGqFXya97GP9q+hUSSRUIGayq2yoy7ZZjaFIV +PYyK7L9rGJXgA6wBfZcFZ84OhZU3au0Jtq5nzVFkn8Zc0bxXbmc1gHY2pIeBbjiP +2CsVTnsl2Fq/ToPBjdKT1RpxtWCcnTNOVfkSWAyGuBYNweV3RY1QSLorLeSUheHo +xJ3GaKWwo/xnfnC6AllLd0KRObn1zeFM78A7SIym5SFd/Wpqu6cWNWDS5q3zRinJ +6MOL6XnAamFnFbLw/eVovGJfbs+Z3e8bY/6SZasCAwEAAaOBrjCBqzAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUiCcXCam2GGCL7Ou6 +9kdZxVJUo7cwHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wNgYDVR0f +BC8wLTAroCmgJ4YlaHR0cDovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS9yb290LmNy +bDAQBgoqhkiG92NkBgIBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEA2jIAlsVUlNM7 +gjdmfS5o1cPGuMsmjEiQzxMkakaOY9Tw0BMG3djEwTcV8jMTOSYtzi5VQOMLA6/6 +EsLnDSG41YDPrCgvzi2zTq+GGQTG6VDdTClHECP8bLsbmGtIieFbnd5G2zWFNe8+ +0OJYSzj07XVaH1xwHVY5EuXhDRHkiSUGvdW0FY5e0FmXkOlLgeLfGK9EdB4ZoDpH +zJEdOusjWv6lLZf3e7vWh0ZChetSPSayY6i0scqP9Mzis8hH4L+aWYP62phTKoL1 +fGUuldkzXfXtZcwxN8VaBOhr4eeIA0p1npsoy0pAiGVDdd3LOiUjxZ5X+C7O0qmS +XnMuLyV1FQ== +-----END CERTIFICATE----- diff --git a/TotalCrossSDK/etc/tools/ipa/dummy.mobileprovision b/TotalCrossSDK/etc/tools/ipa/dummy.mobileprovision new file mode 100644 index 0000000000..a6e5a65296 --- /dev/null +++ b/TotalCrossSDK/etc/tools/ipa/dummy.mobileprovision @@ -0,0 +1,39 @@ + + + + + ApplicationIdentifierPrefix + + ABCDEFGHIJ + + CreationDate + 2012-07-01T00:00:00Z + Entitlements + + application-identifier + ABCDEFGHIJ.com.application.name + get-task-allow + + keychain-access-groups + + ABCDEFGHIJ.* + + + ExpirationDate + 2015-03-27T00:00:00Z + Name + Dummy Provision + ProvisionsAllDevices + + TeamIdentifier + + ABCDEFGHIJ + + TimeToLive + 999 + UUID + F47EEDA3-835B-4917-8C9C-ED1893906E00 + Version + 1 + + diff --git a/TotalCrossSDK/etc/tools/ipa/dummyStore.p12 b/TotalCrossSDK/etc/tools/ipa/dummyStore.p12 new file mode 100644 index 0000000000..b2ce63a81d Binary files /dev/null and b/TotalCrossSDK/etc/tools/ipa/dummyStore.p12 differ diff --git a/TotalCrossSDK/proguard.txt b/TotalCrossSDK/proguard.txt index ae4172ab55..33fb4d31df 100644 --- a/TotalCrossSDK/proguard.txt +++ b/TotalCrossSDK/proguard.txt @@ -16,10 +16,6 @@ -assumenosideeffects class * { ! *4D(...); } --keep public class litebase.** { - public protected ; - public protected ; -} # keep classes on "totalcross" package -keep class totalcross.** { diff --git a/TotalCrossSDK/src/main/java/jdkcompat/io/InputStreamReader4D.java b/TotalCrossSDK/src/main/java/jdkcompat/io/InputStreamReader4D.java index 3f920adb3b..955b223b01 100644 --- a/TotalCrossSDK/src/main/java/jdkcompat/io/InputStreamReader4D.java +++ b/TotalCrossSDK/src/main/java/jdkcompat/io/InputStreamReader4D.java @@ -52,8 +52,10 @@ //import java.nio.charset.CoderResult; //import java.nio.charset.CodingErrorAction; import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; -import totalcross.sys.CharacterConverter; +import totalcross.sys.AbstractCharacterConverter; import totalcross.sys.Convert; import totalcross.sys.Vm; @@ -172,6 +174,7 @@ public InputStreamReader4D(InputStream in) { throw new NullPointerException(); } this.in = in; + this.cconv = Convert.charConverter; // try // { // encoding = EncodingHelper.getDefaultEncoding();//SystemProperties.getProperty("file.encoding"); @@ -217,14 +220,15 @@ public InputStreamReader4D(InputStream in) { * @exception UnsupportedEncodingException If the encoding scheme * requested is not available. */ - // public InputStreamReader4D(InputStream in, String encoding_name) - // throws UnsupportedEncodingException - // { - // if (in == null - // || encoding_name == null) - // throw new NullPointerException(); - // - // this.in = in; + public InputStreamReader4D(InputStream in, String encoding_name) + throws UnsupportedEncodingException + { + if (in == null + || encoding_name == null) + throw new NullPointerException(); + + this.in = in; + this.cconv = (AbstractCharacterConverter) Charset.forName(encoding_name); // // Don't use NIO if avoidable // if(EncodingHelper.isISOLatin1(encoding_name)) // { @@ -253,7 +257,7 @@ public InputStreamReader4D(InputStream in) { // maxBytesPerChar = 1f; // decoder = null; // } - // } + } /** * Creates an InputStreamReader that uses a decoder of the given @@ -262,10 +266,11 @@ public InputStreamReader4D(InputStream in) { * * @since 1.4 */ - // public InputStreamReader4D(InputStream in, Charset charset) { - // if (in == null) - // throw new NullPointerException(); - // this.in = in; + public InputStreamReader4D(InputStream in, Charset charset) { + if (in == null) + throw new NullPointerException(); + this.in = in; + this.cconv = (AbstractCharacterConverter) charset; // decoder = charset.newDecoder(); // // try { @@ -278,7 +283,7 @@ public InputStreamReader4D(InputStream in) { // decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); // decoder.reset(); // encoding = EncodingHelper.getOldCanonical(charset.name()); - // } + } /** * Creates an InputStreamReader that uses the given charset decoder @@ -339,10 +344,10 @@ public void close() throws java.io.IOException { * * @return The current encoding name */ - // public String getEncoding() - // { - // return in != null ? encoding : null; - // } + public String getEncoding() + { + return in != null ? cconv.name() : null; + } /** * This method checks to see if the stream is ready to be read. It @@ -367,7 +372,7 @@ public boolean ready() throws java.io.IOException { private static final int BUFFER_SIZE = 16 * 1024; private byte[] readBytesBuff = new byte[BUFFER_SIZE]; - private CharacterConverter cconv = (CharacterConverter) Convert.charsetForName("ISO-8859-1"); + private final AbstractCharacterConverter cconv; /** * This method reads up to length characters from the stream into @@ -384,18 +389,29 @@ public boolean ready() throws java.io.IOException { */ @Override public int read(char[] b, int off, int len) throws java.io.IOException { + if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } + synchronized (lock) { if (eos) { return -1; } - int arraySize = b.length; - int maxReadable = Math.min(len, arraySize - off); - int remaining = maxReadable; - + int remaining = len; int offsetUsed = off; int totalRead = 0; + int available = decodedSize - decodedReadPos; + if (available > 0) { + int size = Math.min(remaining, available); + Vm.arrayCopy(charsDecodedRead, decodedReadPos, b, offsetUsed, size); + decodedReadPos += size; + offsetUsed += size; + remaining -= size; + totalRead += size; + } + while (remaining > 0) { ensureFetch(); if (decodedSize <= 0) { @@ -426,9 +442,24 @@ public int read(char[] b, int off, int len) throws java.io.IOException { */ @Override public int read() throws java.io.IOException { - char[] buf = new char[1]; - int count = read(buf, 0, 1); - return count > 0 ? buf[0] : -1; + synchronized (lock) { + if (eos) { + return -1; + } + + if (decodedReadPos < decodedSize) { + return charsDecodedRead[decodedReadPos++]; + } + + do { + ensureFetch(); + if (eos) { + return -1; + } + } while (decodedReadPos >= decodedSize); + + return charsDecodedRead[decodedReadPos++]; + } } /** diff --git a/TotalCrossSDK/src/main/java/tc/Deploy.java b/TotalCrossSDK/src/main/java/tc/Deploy.java index ea8d4513d4..97433609c0 100644 --- a/TotalCrossSDK/src/main/java/tc/Deploy.java +++ b/TotalCrossSDK/src/main/java/tc/Deploy.java @@ -11,6 +11,7 @@ import java.util.Calendar; import tc.tools.JarClassPathLoader; +import tc.tools.RegisterSDK; import tc.tools.AnonymousUserData; import tc.tools.converter.J2TC; import tc.tools.deployer.Bitmaps; @@ -25,6 +26,7 @@ import tc.tools.deployer.Deployer4LinuxArm64; import tc.tools.deployer.DeployerException; import tc.tools.deployer.Utils; +import totalcross.sys.Convert; import totalcross.util.ElementNotFoundException; import totalcross.util.IntHashtable; @@ -50,6 +52,7 @@ public static void main(String[] args) throws Exception { public static final int BUILD_ALL = 0xFFFF; private boolean waitIfError; // guich@tc111_24 + public static String activationKey; public Deploy(String[] args) throws Exception { try { @@ -148,7 +151,7 @@ public Deploy(String[] args) throws Exception { if (!DeploySettings.testClass && DeploySettings.showBBPKGName != null) { System.out.println("\nThe BlackBerry's " + DeploySettings.showBBPKGName + ".cod package was created. The files will be copied to the folder \"" + DeploySettings.showBBPKGRoot - + "\". Remember that the files are not uninstalled when your application is removed, and, if they are Litebase tables, be sure to DONT CHANGE the default database path, or the installed files will not be found."); + + "\". Remember that the files are not uninstalled when your application is removed."); } if (DeploySettings.mainClassName == null && DeploySettings.isJarOrZip) { String fn = Utils.getFileName(fileName); @@ -185,20 +188,20 @@ public Deploy(String[] args) throws Exception { } private void addJars() throws IOException { - //flsobral@tc210: dynamically load some useful libs for handling files and compression - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/bouncycastle/bcprov-jdk15on-1.56.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/bouncycastle/bcpkix-jdk15on-1.56.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/commons/commons-io-2.2.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/commons/commons-compress-1.4.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/truezip/truezip-driver-file-7.5.1.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/truezip/truezip-driver-zip-7.5.1.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/truezip/truezip-file-7.5.1.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/truezip/truezip-kernel-7.5.1.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/truezip/truezip-swing-7.5.1.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "tools/ipa/dd-plist.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/totalcross-annotations.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/asm-5.2.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/asm-tree-5.2.jar"); + File libsFile = new File(DeploySettings.distDir, "libs"); + JarClassPathLoader.addJar(libsFile, "bcprov-jdk15on"); + JarClassPathLoader.addJar(libsFile, "bcpkix-jdk15on"); + JarClassPathLoader.addJar(libsFile, "commons-io"); + JarClassPathLoader.addJar(libsFile, "commons-compress"); + JarClassPathLoader.addJar(libsFile, "truezip-driver-file"); + JarClassPathLoader.addJar(libsFile, "truezip-driver-zip"); + JarClassPathLoader.addJar(libsFile, "truezip-file"); + JarClassPathLoader.addJar(libsFile, "truezip-kernel"); + JarClassPathLoader.addJar(libsFile, "truezip-swing"); + JarClassPathLoader.addJar(libsFile, "dd-plist"); + JarClassPathLoader.addJar(libsFile, "totalcross-annotations"); + JarClassPathLoader.addJar(libsFile, "asm"); + JarClassPathLoader.addJar(libsFile, "asm-tree"); } private void showException(Throwable e, String extraMsg) { @@ -280,7 +283,14 @@ private int parseOptions(String[] args) throws Exception { } else if (first == '/') { switch (op.charAt(1)) { case 'a': - if (op.equals("/autostart")) { + if ("/android_signing_config".equals(op)) { + Deployer4Android.signingPropertiesPath = args[++i]; + } else if ("/android_manage_external_storage".equals(op)) { + Deployer4Android.permissionManageExternalStorage = true; + } else if ("/android_request_install_packages".equals(op)) { + Deployer4Android.permissionRequestInstallPackages = true; + } + else if (op.equals("/autostart")) { System.out.println("Autostart on Android's boot"); DeploySettings.autoStart = true; } else { @@ -359,7 +369,15 @@ public boolean accept(File dir, String fileName) { DeploySettings.quiet = false; break; case 'r': - i++; // Ignore next argument + String key = args[++i].toUpperCase(); + if (key.startsWith("%")) { + key = System.getenv(key.substring(1, key.length() - 1)); + } + if (key != null && key.length() != 24) { + throw new DeployerException( + "The key must be specified in the following format: XXXXXXXXXXXXXXXXXXXXXXXX; optionally, you can use %key% to refer to an environment variable"); + } + activationKey = key; break; case 't': DeploySettings.testClass = true; @@ -381,11 +399,7 @@ public boolean accept(File dir, String fileName) { System.out.println("Creating single installation package"); break; case 'i': - if (op.equals("/include_sms")) { - DeploySettings.includeSms = true; - } else { - DeploySettings.installPlatforms = args[++i].toLowerCase() + ","; - } + DeploySettings.installPlatforms = args[++i].toLowerCase() + ","; break; default: @@ -393,6 +407,13 @@ public boolean accept(File dir, String fileName) { } } } + if (activationKey == null) { + activationKey = RegisterSDK.getStoredActivationKey(); + } + if (activationKey != null) { + DeploySettings.rasKey = Convert.hexStringToBytes(activationKey, true); + System.out.println("The application was signed with the given registration key."); + } return options; } @@ -429,23 +450,27 @@ private void usage() { + " /d : Dump generated opcodes for the program\n" + " /d2 : Dump generated opcodes, including java bytecodes\n" + " /i platforms : install the file after generating it; platforms is a list of comma-separated platforms. Supports: android. E.G.: /i android\n" - + " /k : Keep the exe and other temporary files during wince generation\n" - + " /kn : As /k, but does not create the cab files for wince\n" - + " /m path : Specifies a path to the mobileprovision and certificate store to deploy an ipa file for iOS. You should also provide a splash.png image with 640x1136.\n" - + " /n name : Override the name of the tcz file with the given name\n" - + " /o path : Override the output folder with the given path (defaults to the current folder)\n" - + " /p : Package the vm and litebase with the application, creating a single installation file. " - + "The SDK must be in the path or in the TOTALCROSS3_HOME environment variable. " - + "The files are always installed at the same folder of the application, so each application will have its own vm.\n" - + " /t : Just test the classes to see if there are any invalid references. Images are not converted, and nothing is written to disk.\n" - + " /v : Verbose output for information messages\n" - + " /w : Waits for a key press if an error occurs\n" - + " /x list : Comma-separated list of class names that must be excluded (in a starts-with manner). E.G.: \"/x com/framework/\" \n" - + "\n" + " The easiest way to create an icon is to provide an 'appicon.png' file of SQUARED size 256x256" - + "which will be automatically converted to the target icon sizes. Put the file in the src folder." - + "If your icon source is VECTOR-based, you may create better icons by exporting to png at the following sizes: " - + "icon76x76.png, icon96x96.png, icon120x120.png, icon144x144.png, icon152x152.png, icon192x192. " - + "Note that TotalCross' algorithm used to downscale the icons, CATMULL-ROM, is the best of the world for that." - + ""); + + " /k : Keep the exe and other temporary files during wince generation\n" + + " /kn : As /k, but does not create the cab files for wince\n" + + " /android_manage_external_storage : Adds the MANAGE_EXTERNAL_STORAGE permission to the manifest. The usage of this permission is subject to restrictions when submitted to the Play Store.\n " + + " /android_request_install_packages : Adds the REQUEST_INSTALL_PACKAGES permission to the manifest. The usage of this permission is subject to restrictions when submitted to the Play Store.\n " + + " /m path : Specifies a path to the mobileprovision and certificate store to deploy an ipa file for iOS. You should also provide a splash.png image with 640x1136.\n" + + " /android_signing_config path : Specifies a path to a properties file containing keystore configuration for signing AAB and APK when deploying for Android.\n" + + " /n name : Override the name of the tcz file with the given name\n" + + " /o path : Override the output folder with the given path (defaults to the current folder)\n" + + " /p : Package the vm with the application, creating a single installation file. " + + "The SDK must be in the path or in the TOTALCROSS3_HOME environment variable. " + + "The files are always installed at the same folder of the application, so each application will have its own vm.\n" + + " /r key : Specify a registration key to be used to activate TotalCross when required\n" + + " /t : Just test the classes to see if there are any invalid references. Images are not converted, and nothing is written to disk.\n" + + " /v : Verbose output for information messages\n" + + " /w : Waits for a key press if an error occurs\n" + + " /x list : Comma-separated list of class names that must be excluded (in a starts-with manner). E.G.: \"/x com/framework/\" \n" + + "\n" + " The easiest way to create an icon is to provide an 'appicon.png' file of SQUARED size 256x256" + + "which will be automatically converted to the target icon sizes. Put the file in the src folder." + + "If your icon source is VECTOR-based, you may create better icons by exporting to png at the following sizes: " + + "icon76x76.png, icon96x96.png, icon120x120.png, icon144x144.png, icon152x152.png, icon192x192. " + + "Note that TotalCross' algorithm used to downscale the icons, CATMULL-ROM, is the best of the world for that." + + ""); } } diff --git a/TotalCrossSDK/src/main/java/tc/Help.java b/TotalCrossSDK/src/main/java/tc/Help.java index d73dffdadf..2304cbdbd9 100644 --- a/TotalCrossSDK/src/main/java/tc/Help.java +++ b/TotalCrossSDK/src/main/java/tc/Help.java @@ -888,7 +888,7 @@ public void initUI() { b2.add(ios = new Check("iOS"), LEFT + 50, AFTER); ios.appId = 2; tip(ios, "Deploys for iOS", "Faz o deploy para iOS"); - b2.add(wmo = new Check("Win Mobile"), AFTER + 50, SAME); + b2.add(wmo = new Check("Win Mobile"), LEFT + 50, AFTER + 25); wmo.appId = 2; tip(wmo, "Deploys for Windows Mobile 5, 6 and 7", "Faz o deploy para Windows Mobile 5, 6 e 7"); b2.add(and = new Check("Android"), SAME, BEFORE - 25); diff --git a/TotalCrossSDK/src/main/java/tc/tools/JarClassPathLoader.java b/TotalCrossSDK/src/main/java/tc/tools/JarClassPathLoader.java index 5063b72825..17960b53bb 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/JarClassPathLoader.java +++ b/TotalCrossSDK/src/main/java/tc/tools/JarClassPathLoader.java @@ -4,9 +4,15 @@ package tc.tools; +import java.io.File; +import java.io.FilenameFilter; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; /** * Utility class that dynamically loads a jar file into the Deploy classpath.
    @@ -21,15 +27,51 @@ public class JarClassPathLoader { private static final Class[] parameters = new Class[] { URL.class }; - public static void addFile(String s) throws java.io.IOException { - java.io.File f = new java.io.File(s); - addFile(f); + public static File findJar(File path, String jarName) { + if (path == null || jarName == null) { + return null; + } + + File dir = path; + if (!dir.isDirectory()) { + return null; + } + + String jarPrefix = jarName + "-"; + File[] jars = dir.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.startsWith(jarPrefix) && name.endsWith(".jar"); + } + }); + + if (jars == null || jars.length == 0) { + return null; + } + + List candidates = new ArrayList(); + Collections.addAll(candidates, jars); + Collections.sort(candidates, new Comparator() { + @Override + public int compare(File left, File right) { + return compareJarVersions(jarName, right.getName(), left.getName()); + } + }); + + return candidates.get(0); } public static void addFile(java.io.File f) throws java.io.IOException { addURL(f.toURI().toURL()); } + public static void addJar(File path, String jarName) throws java.io.IOException { + File jar = findJar(path, jarName); + if (jar != null) { + addFile(jar); + } + } + public static void addURL(URL u) throws java.io.IOException { ClassLoader cl = ClassLoader.getSystemClassLoader(); @@ -46,4 +88,83 @@ public static void addURL(URL u) throws java.io.IOException { } } } -} \ No newline at end of file + + private static int compareJarVersions(String jarName, String leftName, String rightName) { + String leftVersion = extractVersion(jarName, leftName); + String rightVersion = extractVersion(jarName, rightName); + return compareVersions(leftVersion, rightVersion); + } + + private static String extractVersion(String jarName, String fileName) { + return fileName.substring(jarName.length() + 1, fileName.length() - ".jar".length()); + } + + private static int compareVersions(String leftVersion, String rightVersion) { + String[] leftParts = leftVersion.split("[^A-Za-z0-9]+"); + String[] rightParts = rightVersion.split("[^A-Za-z0-9]+"); + int length = Math.max(leftParts.length, rightParts.length); + + for (int i = 0; i < length; i++) { + String leftPart = i < leftParts.length ? leftParts[i] : "0"; + String rightPart = i < rightParts.length ? rightParts[i] : "0"; + int comparison = compareVersionPart(leftPart, rightPart); + if (comparison != 0) { + return comparison; + } + } + + return leftVersion.compareTo(rightVersion); + } + + private static int compareVersionPart(String leftPart, String rightPart) { + boolean leftNumber = isNumber(leftPart); + boolean rightNumber = isNumber(rightPart); + + if (leftNumber && rightNumber) { + return compareNumberStrings(leftPart, rightPart); + } + + if (leftNumber) { + return 1; + } + + if (rightNumber) { + return -1; + } + + return leftPart.compareToIgnoreCase(rightPart); + } + + private static boolean isNumber(String value) { + if (value.length() == 0) { + return false; + } + + for (int i = 0; i < value.length(); i++) { + if (!Character.isDigit(value.charAt(i))) { + return false; + } + } + + return true; + } + + private static int compareNumberStrings(String leftNumber, String rightNumber) { + String left = stripLeadingZeroes(leftNumber); + String right = stripLeadingZeroes(rightNumber); + + if (left.length() != right.length()) { + return left.length() - right.length(); + } + + return left.compareTo(right); + } + + private static String stripLeadingZeroes(String value) { + int index = 0; + while (index < value.length() - 1 && value.charAt(index) == '0') { + index++; + } + return value.substring(index); + } +} diff --git a/TotalCrossSDK/src/main/java/tc/tools/RegisterSDK.java b/TotalCrossSDK/src/main/java/tc/tools/RegisterSDK.java new file mode 100644 index 0000000000..8161b7df9c --- /dev/null +++ b/TotalCrossSDK/src/main/java/tc/tools/RegisterSDK.java @@ -0,0 +1,318 @@ +package tc.tools; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +import net.harawata.appdirs.AppDirsFactory; +import tc.tools.deployer.Utils; + +public final class RegisterSDK { + private static final String MAGIC = "T0T@LCR0$$"; + private static final int DATE_MASK = 0xBADCFE; + + private static final int INVALID_DATE = -1000; + private static final int INVALID_REGISTRATION_KEY = -1001; + private static final int EXPIRED_CONTRACT = -1002; + private static final int NO_MORE_DEVELOPERS = -1003; + private static final int CONTRACT_NOT_ACTIVE = -1004; + private static final int INVALID_COMPANY = -1005; + private static final int CONTRACT_DEACTIVATED = -1006; + private static final int DEVICE_DISABLED = -1007; + + private String user, home, key; + private int today; + private File flicense; + + public RegisterSDK(String key, String user) throws Exception { + this(key, false); + storeActivationKey(key); + } + + public RegisterSDK(String newkey) throws Exception { + this(newkey, false); + } + + private RegisterSDK(String key, boolean force) throws Exception { + this.key = key; + if (key.startsWith("54435354")) { + throw new RegisterSDKException("Free license has expired"); + } + if (key.length() != 24) { + throw new RegisterSDKException("The key is incorrect"); + } + today = Utils.getToday(); + user = System.getProperty("user.name"); + home = System.getProperty("user.home"); + flicense = new File(home + "/" + key + ".key"); + if (force || !flicense.exists()) { + updateLicense(); + } + int ret = checkLicense(); + if (ret == EXPIRED || ret == OLD || ret == INVALID) // if expired or last activation occured after 8 hours + { + updateLicense(); + if (checkLicense() == EXPIRED) { + throw new RegisterSDKException("The license is expired"); + } + } + if (giexp != -1) { + System.out.println("Next SDK expiration date: " + showDate(giexp)); + } + } + + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("Format: tc.tools.RegisterSDK "); + } else { + try { + new RegisterSDK(args[0], true); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + private byte[] doCrypto(boolean encrypt, byte[] in) throws NoSuchAlgorithmException, NoSuchPaddingException, + InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + char[] keystr = (key + "CAFEBABE").toCharArray(); + byte[] keybytes = new byte[16]; + for (int i = 0, n = keystr.length; i < n; i += 2) { + keybytes[i / 2] = (byte) Integer.valueOf("" + keystr[i] + keystr[i + 1], 16).intValue(); + } + + Key secretKey = new SecretKeySpec(keybytes, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(in); + } + + private static final int EXPIRED = 0; + private static final int OLD = -1; + private static final int VALID = -2; + private static final int INVALID = -3; + + private int giexp = -1; + + private int checkLicense() throws Exception { + try { + // read license file + InputStream in = new FileInputStream(flicense); + byte[] fin = new byte[in.available()]; + in.read(fin); + in.close(); + // use the key passed to launcher + byte[] bin = doCrypto(false, fin); + DataInputStream ds = new DataInputStream(new ByteArrayInputStream(bin)); + // skip trash + int xdataLen = Math.abs(key.hashCode() % 1000); + ds.skip(xdataLen); + // checks if the magic equals + boolean magicEquals = false; + try { + String magic = ds.readUTF(); + magicEquals = magic.equals(MAGIC); + } catch (Exception e) { + } + if (!magicEquals) { + System.out.println( + "This license key does not correspond to the stored key! Requesting new license with current key..."); + return INVALID; + } + // read the rest of stored data and compare with current values + String skv = ds.readUTF(); + HashMap kv = Utils.pipeSplit(skv); + String storedUser = kv.get("user"); + String storedFolder = kv.get("home"); + int iexp = ds.readInt(); + // check if user has changed the time + long storedTimestamp = ds.readLong(); + long currentTimestamp = System.currentTimeMillis(); + long hoursElapsed = (currentTimestamp - storedTimestamp) / (60 * 60 * 1000); + if (hoursElapsed < 0) { + throw new RegisterSDKException("The computer's time is invalid."); + } + boolean expired = today >= iexp; + + int diffU = storedUser.equals(user) ? 0 : 1; + int diffF = storedFolder.equals(home) ? 0 : 2; + int diff = diffU | diffF; + + if (diff != 0) { + System.out.println("The license parameters have changed (#" + diff + "). A new license will be requested"); + } else { + giexp = iexp; + } + return diff != 0 ? INVALID : expired ? EXPIRED : hoursElapsed > 12 ? OLD : VALID; + } catch (FileNotFoundException fnfe) { + throw new RegisterSDKException("License not found! Check if this computer's internet is available."); + } catch (javax.crypto.BadPaddingException bpe) { + System.out.println( + "This license key does not correspond to the stored key! Requesting new license with current key..."); + return INVALID; + } + } + + private void updateLicense() throws Exception { + try { + // connect to the registration service and validate the key and mac. + int expdate = getExpdateFromServer(); + if (expdate <= 0) { + switch (expdate) { + case INVALID_DATE: + throw new RegisterSDKException("Please update your computer's date."); + case INVALID_REGISTRATION_KEY: + throw new RegisterSDKException("The registration key is invalid."); + case EXPIRED_CONTRACT: + throw new RegisterSDKException("The contract is EXPIRED."); + case NO_MORE_DEVELOPERS: + throw new RegisterSDKException("The number of active developers has been reached."); + case CONTRACT_NOT_ACTIVE: + throw new RegisterSDKException( + "This contract is not yet active. Please send email to renato@totalcross.com with your registration key."); + case INVALID_COMPANY: + throw new RegisterSDKException("Invalid company"); + case CONTRACT_DEACTIVATED: + throw new RegisterSDKException( + "The contract is suspended due to payment reasons. Please contact renato@totalcross.com."); + case DEVICE_DISABLED: + throw new RegisterSDKException("This device is disabled."); + } + } else { + ByteArrayOutputStream baos = new ByteArrayOutputStream(128); + DataOutputStream ds = new DataOutputStream(baos); + // generate fake data to put before and at end of important data + java.util.Random r = new java.util.Random(expdate); + int xdataLen = Math.abs(key.hashCode() % 1000); + byte[] xdata = new byte[xdataLen]; + r.nextBytes(xdata); + ds.write(xdata); + // write important data + ds.writeUTF(MAGIC); + ds.writeUTF(Utils.pipeConcat("home", home, "user", user)); // MUST BE ALPHABETHICAL ORDER! + ds.writeInt(expdate); + ds.writeLong(System.currentTimeMillis()); + // write fake data at end + r.nextBytes(xdata); + ds.write(xdata); + ds.close(); + byte[] bytes = baos.toByteArray(); + byte[] cript = doCrypto(true, bytes); + OutputStream out = new FileOutputStream(flicense); + out.write(cript); + out.close(); + } + } catch (RegisterSDKException rse) { + throw rse; + } catch (UnknownHostException uhe) { + System.out.println( + "INTERNET NOT AVAILABLE! TotalCross requires activation of the SDK. Once the date below is reached, the SDK will block."); + } catch (Throwable e) { + System.out.println("Exception during license update: " + e.getClass().getSimpleName() + " - " + e.getMessage()); + } + } + + private int getExpdateFromServer() throws IOException { + // zip data + ByteArrayOutputStream bas = new ByteArrayOutputStream(256); + Deflater dd = new Deflater(9, false); + DeflaterOutputStream def = new DeflaterOutputStream(bas, dd); + DataOutputStream ds = new DataOutputStream(def); + ds.writeUTF(key); + ds.writeUTF(Utils.pipeConcat("home", home, "user", user)); // MUST BE ALPHABETHICAL ORDER! + ds.writeInt(today); + def.finish(); + dd.end(); + byte[] bytes = bas.toByteArray(); + + // prepare connection + URLConnection con = new URL("http://www.superwaba.net/SDKRegistrationService/SDKRegistration").openConnection(); + con.setRequestProperty("Request-Method", "POST"); + con.setConnectTimeout(5000); + con.setUseCaches(false); + con.setDoOutput(true); + con.setDoInput(true); + + // send data + OutputStream os = con.getOutputStream(); + os.write(bytes); + os.close(); + + // get response - the expiration date or a negative value for error codes + InputStream in = con.getInputStream(); + DataInputStream dis = new DataInputStream(in); + int expdate = dis.readInt() ^ DATE_MASK; + int expcontract = dis.readInt() ^ DATE_MASK; + if (expcontract != 0) { + System.out.println("INFO: the contract will expire at " + showDate(expcontract)); + } + dis.close(); + in.close(); + + return expdate; + } + + private String showDate(int d) { + int day = d % 100; + int month = d / 100 % 100; + int year = d / 10000; + return day + " " + totalcross.util.Date.monthNames[month] + ", " + year; + } + + public static String getStoredActivationKey() { + String activationKey = null; + try (FileInputStream fis = new FileInputStream( + new File(AppDirsFactory.getInstance().getUserDataDir(null, null, "TotalCross"), "key.dat"))) { + byte[] b = new byte[24]; + fis.read(b); + activationKey = new String(b, "UTF-8"); + } catch (FileNotFoundException e) { + } catch (java.io.IOException e) { + e.printStackTrace(); + } + return activationKey; + } + + private static void storeActivationKey(String activationKey) throws IOException { + File parent = new File(AppDirsFactory.getInstance().getUserDataDir(null, null, "TotalCross")); + if (!parent.exists()) { + parent.mkdirs(); + } + try (FileOutputStream fos = new FileOutputStream(new File(parent, "key.dat"))) { + fos.write(activationKey.getBytes(Charset.forName("UTF-8"))); + } + } +} + +class RegisterSDKException extends Exception { + public RegisterSDKException(String s) { + super(s); + } + + public RegisterSDKException() { + } +} diff --git a/TotalCrossSDK/src/main/java/tc/tools/converter/GlobalConstantPool.java b/TotalCrossSDK/src/main/java/tc/tools/converter/GlobalConstantPool.java index e629116832..316798ae2d 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/converter/GlobalConstantPool.java +++ b/TotalCrossSDK/src/main/java/tc/tools/converter/GlobalConstantPool.java @@ -101,6 +101,7 @@ public static void init() { htMtd.clear(); htSF.clear(); htIF.clear(); + htCls.clear(); vI32.removeAllElements(); vI64.removeAllElements(); vDbl.removeAllElements(); @@ -109,6 +110,7 @@ public static void init() { vMtd.removeAllElements(); vSF.removeAllElements(); vIF.removeAllElements(); + vCls.removeAllElements(); // make all arrays start from 1 dCount = lCount = 1; vI32.addElement(temp); diff --git a/TotalCrossSDK/src/main/java/tc/tools/converter/J2TC.java b/TotalCrossSDK/src/main/java/tc/tools/converter/J2TC.java index 60e3ed7b2a..aff9949a8c 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/converter/J2TC.java +++ b/TotalCrossSDK/src/main/java/tc/tools/converter/J2TC.java @@ -934,7 +934,7 @@ private static int processFiles(Vector vin, Vector vout) throws Exception { } } else if (!DeploySettings.testClass) { ByteArrayStream basz = new ByteArrayStream(bytes); - print = !name.equals("tcparms.bin") && !name.equals(DeploySettings.TCAPP_PROP); + print = !name.equals("tckey.bin") && !name.equals("tcparms.bin") && !name.equals(DeploySettings.TCAPP_PROP); if (print) { System.out.print("Adding " + name); @@ -1155,8 +1155,9 @@ public static void process(String fName, int options) throws Exception { Map classes = new HashMap<>(); DeploySettings.isJarOrZip = fLow.endsWith(".zip") || fLow.endsWith(".jar"); if (fLow.endsWith(".jar")) { - JarClassPathLoader.addFile(fName); - classes = getJavaClassFromJar(new JarFile(new java.io.File(fName))); + java.io.File jarFile = new java.io.File(fName); + JarClassPathLoader.addFile(jarFile); + classes = getJavaClassFromJar(new JarFile(jarFile)); } if (DeploySettings.isJarOrZip) // process bat of files? { @@ -1247,6 +1248,28 @@ public static void process(String fName, int options) throws Exception { if (DeploySettings.currentDir == null) { DeploySettings.currentDir = "./"; } + if (DeploySettings.rasKey != null && !DeploySettings.isTotalCrossJarDeploy + && (DeploySettings.isMainWindow || DeploySettings.isService || DeploySettings.isMainClass)) { // registration key was specified - guich@tc310: only if not deploying the sdk and if its not a library + AESCipher cipher = new AESCipher(); + AESKey key = new AESKey(new byte[] { (byte) 0x06, (byte) 0x05, (byte) 0xF4, (byte) 0xF0, (byte) 0xF4, + (byte) 0x08, (byte) 0x01, (byte) 0x09, (byte) 0xF7, (byte) 0x09, (byte) 0xFE, (byte) 0xFC, (byte) 0xF5, + (byte) 0x04, (byte) 0x00, (byte) 0x0B }); + + cipher.reset(Cipher.OPERATION_ENCRYPT, key, Cipher.CHAINING_CBC, null, Cipher.PADDING_PKCS5); + cipher.update(DeploySettings.rasKey); + byte[] riv = cipher.getIV(); + byte[] out = cipher.getOutput(); + + ByteArrayStream bas = new ByteArrayStream(128); + DataStream ds = new DataStream(bas); + ds.writeInt(riv.length); + ds.writeBytes(riv); + ds.writeInt(out.length); + ds.writeBytes(out); + + byte[] enc = bas.toByteArray(); + vin.addElement(new TCZ.Entry(enc, "tckey.bin", enc.length)); + } if (DeploySettings.mainClassName != null && !DeploySettings.isTotalCrossJarDeploy && DeploySettings.isMainWindow) //flsobral@tc126: test for mainClassName instead of mainClassDir. The later is always null when deploy is used with a zip/jar file. This fixes third-party server activation when the application is deployed from a jar file. - guich@tc310 only if not deploying the sdk and if its not a library { Hashtable htVmParams = new Hashtable(4); diff --git a/TotalCrossSDK/src/main/java/tc/tools/converter/java/JavaCode.java b/TotalCrossSDK/src/main/java/tc/tools/converter/java/JavaCode.java index 32d669b8df..b7b38f0575 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/converter/java/JavaCode.java +++ b/TotalCrossSDK/src/main/java/tc/tools/converter/java/JavaCode.java @@ -93,8 +93,7 @@ public JavaCode(JavaMethod method, DataStream ds, JavaConstantPool cp) throws to } } String className = method.classOfMethod.className.replace('/', '.'); - if (!lineNumberWarned && lineNumberLine == null && !className.startsWith("totalcross.") - && !className.startsWith("litebase.")) // guich@tc110_68 + if (!lineNumberWarned && lineNumberLine == null && !className.startsWith("totalcross.")) // guich@tc110_68 { lineNumberWarned = true; System.out.println( diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/DeploySettings.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/DeploySettings.java index d82c0e5f2a..6b2a8651f3 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/DeploySettings.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/DeploySettings.java @@ -24,9 +24,8 @@ public class DeploySettings { public static String[] tczs; - // constants for including the vm and/or litebase in a package + // constants for including the vm in a package public static boolean packageVM; - public static boolean includeSms; public static String folderTotalCross3DistVM; public static String tczFileName; @@ -51,6 +50,7 @@ public class DeploySettings { public static boolean autoStart; + public static byte[] rasKey; public static boolean autoSign; public static String autoSignPassword; public static boolean quiet = true; // to set to false, pass /v(erbose) to tc.Deploy @@ -99,7 +99,6 @@ public static List getDefaultTczs() { defaultTczs.add(new java.io.File(DeploySettings.folderTotalCross3DistVM, "TCBase.tcz")); defaultTczs.add(new java.io.File(DeploySettings.folderTotalCross3DistVM, "TCUI.tcz")); defaultTczs.add(new java.io.File(DeploySettings.folderTotalCross3DistVM, "TCFont.tcz")); - defaultTczs.add(new java.io.File(DeploySettings.folderTotalCross3DistVM, "LitebaseLib.tcz")); defaultTczs.add(new java.io.File(Convert.appendPath(DeploySettings.etcDir, "fonts"), "Material Icons.tcz")); } return defaultTczs; @@ -130,7 +129,6 @@ public static void init() throws Exception { exclusionList.addElement("java/"); exclusionList.addElement("["); exclusionList.addElement("tc/tools/"); - exclusionList.addElement("litebase/"); exclusionList.addElement("ras/"); exclusionList.addElement("net/rim/"); @@ -201,24 +199,27 @@ public static void init() throws Exception { System.out.println("Etc directory: " + (etcDir != null ? etcDir : "not found")); // keep this always visible, its a very important information System.out.println("Classpath: " + cp0); - // find the demo and release folders for totalcross and litebase - String f = System.getenv("TOTALCROSS3_HOME"); - if (f == null) { - f = System.getenv("TOTALCROSS3"); + // find the demo and release folders for totalcross + if (distDir != null) { + folderTotalCross3DistVM = distDir + "vm/"; } - if (f != null) { - folderTotalCross3DistVM = Convert.appendPath(f, "dist/vm/"); - if (!new File(folderTotalCross3DistVM).isDir()) { - folderTotalCross3DistVM = Convert.appendPath(f, "vm/"); + + if (folderTotalCross3DistVM == null) { + String f = System.getenv("TOTALCROSS3_HOME"); + if (f == null) { + f = System.getenv("TOTALCROSS3"); + } + if (f != null) { + folderTotalCross3DistVM = Convert.appendPath(f, "dist/vm/"); if (!new File(folderTotalCross3DistVM).isDir()) { - folderTotalCross3DistVM = f; + folderTotalCross3DistVM = Convert.appendPath(f, "vm/"); + if (!new File(folderTotalCross3DistVM).isDir()) { + folderTotalCross3DistVM = f; + } } } } - if (folderTotalCross3DistVM == null) { - folderTotalCross3DistVM = distDir + "vm/"; - } // check if folders exist if (folderTotalCross3DistVM == null || !new File(folderTotalCross3DistVM).exists()) { folderTotalCross3DistVM = null; diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4Android.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4Android.java index 568e958208..0d2dcdba3e 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4Android.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4Android.java @@ -4,37 +4,44 @@ // SPDX-License-Identifier: LGPL-2.1-only package tc.tools.deployer; +import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Properties; import java.util.Set; import java.util.zip.Adler32; import java.util.zip.CRC32; +import java.util.zip.ZipInputStream; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.bouncycastle.crypto.RuntimeCryptoException; +import de.schlichtherle.truezip.file.TFile; +import de.schlichtherle.truezip.file.TVFS; import de.schlichtherle.truezip.zip.ZipEntry; -import de.schlichtherle.truezip.zip.ZipFile; import de.schlichtherle.truezip.zip.ZipOutputStream; -import tc.tools.converter.bb.JavaAttribute; -import tc.tools.converter.bb.JavaClass; -import tc.tools.converter.bb.JavaConstant; -import tc.tools.converter.bb.JavaField; -import tc.tools.converter.bb.JavaMethod; -import tc.tools.converter.bb.attribute.Code; -import tc.tools.converter.bb.attribute.LocalVariableTable; -import tc.tools.converter.bb.attribute.SourceFile; -import tc.tools.converter.bb.constant.Class; -import tc.tools.converter.bb.constant.Integer; -import tc.tools.converter.bb.constant.NameAndType; -import tc.tools.converter.bb.constant.UTF8; +import totalcross.sys.Convert; +import totalcross.sys.Vm; import totalcross.util.Hashtable; import totalcross.util.Vector; @@ -79,7 +86,7 @@ a zip and store the TCZ (and also files from android.pkg) inside that zip. When way to delete the zip inside tha APK after the installation. - Also: all TotalCross programs must be signed with the same key, otherwise the vm and litebase + Also: all TotalCross programs must be signed with the same key, otherwise the vm will not be able to read data from the program's folder. @@ -101,241 +108,426 @@ a zip and store the TCZ (and also files from android.pkg) inside that zip. When */ public class Deployer4Android { - private static final boolean DEBUG = false; - private static String targetDir, sourcePackage, targetPackage, targetTCZ, jarOut, fileName; + public static String signingPropertiesPath = null; + public static boolean permissionManageExternalStorage = false; + public static boolean permissionRequestInstallPackages = false; + private final Properties signingConfig = new Properties(); + private final String targetDir; + private final String targetTCZ; private String tcFolder; - private boolean singleApk; - private boolean includeSms; - byte[] buf = new byte[8192]; + abstract static class ProtocExec { + final static String NAME = "protoc"; + final static String VERSION = "21.0"; + final static String BASE_URL = "https://github.com/protocolbuffers/protobuf/releases/download/v"; + + public static String getExecutable() { + String osName = null; + + if (DeploySettings.isWindows()) { + osName = "win64"; + } else if (DeploySettings.isMac()) { + osName = "osx-universal_binary"; + } else { + String osArch = System.getProperty("os.arch"); + if (osArch.equals("aarch64") || osArch.equals("arm64")) { + osArch = "aarch_64"; + } else if (osArch.equals("x86_64") || osArch.equals("amd64")) { + osArch = "x86_64"; + } else { + System.out.println("Couldn't detect system architecture, trying with x86_64"); + osArch = "x86_64"; + } + osName = "linux-" + osArch; + } + + final String protocString = NAME + '-' + VERSION + '-' + osName; + final File protocBaseFolder = new File(DeploySettings.etcDir, "tools/android/protoc"); + protocBaseFolder.mkdirs(); + + final File protocExecutable = new File(protocBaseFolder, DeploySettings.appendDotExe("bin/protoc")); + if (!protocExecutable.exists()) { + final String downloadUrl = BASE_URL + VERSION + "/" + protocString + ".zip"; + // download and unzip protoc + try { + System.out.println("Downloading protoc..."); + downloadAndUnzip(downloadUrl, protocBaseFolder.getAbsolutePath()); + } catch (Exception e) { + throw new RuntimeException("Failed to download protoc at: " + downloadUrl + + " ; You may download it yourself and unzip the contents into the folder: " + + protocBaseFolder.getAbsolutePath(), e); + } + + if (DeploySettings.isMac()) { + try { + // Remove quarentine (macOS) + new ProcessBuilder("xattr", "-d", "com.apple.quarantine", protocExecutable.getAbsolutePath()) + .start() + .waitFor(); + } catch (InterruptedException | IOException e) { + throw new RuntimeException("Failed to remove protoc from quarentine (xattr -d com.apple.quarantine " + + protocExecutable.getAbsolutePath() + ")", e); + } + } + if (!DeploySettings.isWindows()) { + try { + // Make executable + Files.setPosixFilePermissions( + Paths.get(protocExecutable.getAbsolutePath()), + PosixFilePermissions.fromString("rwxr-xr-x")); + } catch (IOException e) { + throw new RuntimeException("Failed to set execution permission to: " + protocExecutable.getAbsolutePath()); + } + } + } + + final String protocExecutablePath = protocExecutable.getAbsolutePath(); + if (!protocExecutable.exists()) { + throw new RuntimeException("Couldn't find protoc at: " + protocExecutablePath); + } + + return protocExecutablePath; + } + + public static void downloadAndUnzip(String fileUrl, String outputDir) throws Exception { + Path tempZip = Files.createTempFile("download", ".zip"); + + downloadFile(fileUrl, tempZip.toFile()); + + Files.createDirectories(Paths.get(outputDir)); + + unzip(tempZip.toString(), outputDir); + } + + static void downloadFile(String fileURL, File outputFile) throws IOException { + URL url = new URL(fileURL); + HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); + httpConn.setRequestProperty("User-Agent", "Mozilla/5.0"); // Prevents some servers from blocking the request + + try (InputStream in = httpConn.getInputStream(); + FileOutputStream out = new FileOutputStream(outputFile)) { + + byte[] buffer = new byte[8192]; + int len; + + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + } + + httpConn.disconnect(); + } + + private static void unzip(String zipFilePath, String destDirectory) throws IOException { + try (ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath))) { + + java.util.zip.ZipEntry entry = zipIn.getNextEntry(); + + while (entry != null) { + String filePath = destDirectory + File.separator + entry.getName(); + + if (!entry.isDirectory()) { + Files.createDirectories(Paths.get(filePath).getParent()); + + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) { + byte[] bytesIn = new byte[4096]; + int read; + while ((read = zipIn.read(bytesIn)) != -1) { + bos.write(bytesIn, 0, read); + } + } + } else { + Files.createDirectories(Paths.get(filePath)); + } + + zipIn.closeEntry(); + entry = zipIn.getNextEntry(); + } + } + } + } + + abstract static class BundletoolExec { + final static String NAME = "bundletool-all"; + final static String VERSION = "1.10.0"; + final static String FILE_NAME = NAME + "-" + VERSION + ".jar"; + final static String DOWNLOAD_URL = + "https://github.com/google/bundletool/releases/download/" + VERSION + "/" + FILE_NAME; + + public static String getJarPath() { + final File androidToolsFolder = new File(DeploySettings.etcDir, "tools/android"); + androidToolsFolder.mkdirs(); + + final File bundletoolJar = new File(androidToolsFolder, FILE_NAME); + if (!bundletoolJar.exists()) { + try { + System.out.println("Downloading bundletool..."); + ProtocExec.downloadFile(DOWNLOAD_URL, bundletoolJar); + } catch (Exception e) { + throw new RuntimeException("Failed to download bundletool at: " + DOWNLOAD_URL + + " ; You may download it yourself and place the jar into the folder: " + + androidToolsFolder.getAbsolutePath(), e); + } + } + + final String bundletoolJarPath = bundletoolJar.getAbsolutePath(); + if (!bundletoolJar.exists()) { + throw new RuntimeException("Couldn't find bundletool at: " + bundletoolJarPath); + } + + return bundletoolJarPath; + } + } public Deployer4Android() throws Exception { + try (FileInputStream fis = new FileInputStream(Utils.findPath(DeploySettings.etcDir + "security/android_keystore.properties", false))) { + signingConfig.load(fis); + } + if (Deployer4Android.signingPropertiesPath != null) { + try (FileInputStream fis = new FileInputStream(Deployer4Android.signingPropertiesPath)) { + signingConfig.load(fis); + } + } targetDir = DeploySettings.targetDir + "android/"; - fileName = DeploySettings.filePrefix; + FileUtils.deleteQuietly(new File(targetDir)); + String fileName = DeploySettings.filePrefix; if (fileName.indexOf(' ') >= 0) { fileName = fileName.replace(" ", ""); } + // source and target packages must have the exact length + final String sourcePackage = "totalcross/android"; + targetTCZ = "app" + DeploySettings.applicationId.toLowerCase(Locale.ROOT); + final String targetPackage = "totalcross/" + targetTCZ; + System.out.println("Android application folder: /data/data/" + (targetPackage.replace('/', '.'))); + + String newPackage = targetPackage.replace('/', '.'); + String newVersion = DeploySettings.appVersion != null ? DeploySettings.appVersion : "1.0"; + String newTitle = DeploySettings.appTitle; + String newSharedId = "totalcross.app.app" + DeploySettings.applicationId.toLowerCase(Locale.ROOT); + // create the output folder File f = new File(targetDir); if (!f.exists()) { f.mkdirs(); } - includeSms = DeploySettings.includeSms; - singleApk = DeploySettings.packageVM; - if (includeSms) { - singleApk = true; - } - if (!singleApk) { - targetPackage = "totalcross/app/" + fileName.toLowerCase(); - if (!DeploySettings.quiet) { - System.out.println("Android application folder: /data/data/" + (targetPackage.replace('/', '.'))); - } - } else { - tcFolder = DeploySettings.folderTotalCross3DistVM + "android/"; - // source and target packages must have the exact length - sourcePackage = "totalcross/android"; - targetTCZ = "app" + DeploySettings.applicationId.toLowerCase(); - targetPackage = "totalcross/" + targetTCZ; - System.out.println("Android application folder: /data/data/" + (targetPackage.replace('/', '.'))); - } - - if (!singleApk) { - createLauncher(); // 1 - jar2dex(); // 2 - } - updateResources(); // 3+4+5 - Utils.jarSigner(fileName + ".apk", targetDir); // 6 - new ZipAlign().zipAlign(new File(targetDir + "/" + fileName + ".apk"), - new File(targetDir + "/" + fileName + "_.apk")); - String apk = targetDir + "/" + fileName + ".apk"; - Utils.copyFile(targetDir + "/" + fileName + "_.apk", apk, true); - String extraMsg = ""; - if (DeploySettings.installPlatforms.indexOf("android,") >= 0) { - extraMsg = callADB(apk); - } - - System.out.println("... Files written to folder " + targetDir + extraMsg); - - } + tcFolder = DeploySettings.folderTotalCross3DistVM + "android/"; - private String callADB(String apk) throws Exception { - String adb = Utils.findPath(DeploySettings.etcDir + "tools/android/adb.exe", false); - if (adb == null) { - throw new DeployerException("File android/adb.exe not found!"); + // 1. locate template + final File templateFile = new File(Convert.appendPath(DeploySettings.folderTotalCross3DistVM, "android/TotalCross.aab")); + if (!templateFile.exists()) { + throw new DeployerException("Missing file: " + templateFile.getPath() + ". Please reinstall the SDK."); } - String message = Utils.exec(new String[] { adb, "install", "-r", apk }, targetDir); - if (message != null && message.indexOf("INPUT:Success") >= 0) { - return " (installed)"; + + // 2. create a temp file and copy the template + final File targetFile = File.createTempFile(DeploySettings.appTitle, ".zip"); + targetFile.deleteOnExit(); + FileUtils.copyFile(templateFile, targetFile); + + // 3. open the temp file with truezip + final TFile targetZip = new TFile(targetFile); + + // 4. locate and copy the AndroidManifest + final File manifestFile = File.createTempFile("AndroidManifest_" + DeploySettings.appTitle, ".xml"); + manifestFile.deleteOnExit(); + TFile b = new TFile(targetZip, "base/manifest/AndroidManifest.xml"); + if (!b.canRead()) { + throw new DeployerException("Template aab is missing the file base/manifest/AndroidManifest.xml"); + } + b.cp(manifestFile); + + // 5. decode the AndroidManifest + // $PROTO_BIN --decode=aapt.pb.XmlNode --proto_path=tools Configuration.proto Resources.proto < $DEST_FOLDER/base/manifest/AndroidManifest.xml > AndroidManifest_temp.xml + final String protocExecutable = ProtocExec.getExecutable(); + String[] decodeCmd = { protocExecutable, "--decode=aapt.pb.XmlNode", "--proto_path=tools", "Configuration.proto", "Resources.proto" }; + + Process process = Runtime.getRuntime().exec(decodeCmd, null, new File(DeploySettings.etcDir, "tools/android")); + final File decodedManifestFile = File.createTempFile("AndroidManifest2_" + DeploySettings.appTitle, ".xml"); + decodedManifestFile.deleteOnExit(); + IOUtils.copy(new FileInputStream(manifestFile), process.getOutputStream()); + process.getOutputStream().close(); + String originalManifest = IOUtils.toString(process.getInputStream(), "UTF-8"); + originalManifest = originalManifest.replaceFirst("(.*\"[pP]ackage\"\\R.*value: )\"[a-z\\.]+\"", "$1\"" + newPackage + "\""); + originalManifest = originalManifest.replaceFirst("(.*\"[vV]ersionName\"\\R.*value: )\"![0-9\\.]+!\"", "$1\"" + newVersion + "\""); + originalManifest = originalManifest.replaceFirst("(.*\"[lL]abel\"\\R.*value: )\"Stub\"", "$1\"" + newTitle + "\""); + originalManifest = originalManifest.replaceFirst("(.*\"[sS]haredUserId\"\\R.*value: )\"[a-z\\.]+\"", "$1\"" + newSharedId + "\""); + boolean isFullScreen = DeploySettings.isFullScreenPlatform(totalcross.sys.Settings.ANDROID); + originalManifest = originalManifest.replaceFirst("(.*\"[vV]alue\"\\R.*value: )\"fullscreen:0+\"", "$1\"fullscreen:" + (isFullScreen ? 1 : 0) + "\""); + originalManifest = originalManifest.replaceFirst("(.*\"[aA]uthorities\"\\R.*value: )\"[a-z\\.]+\"", "$1\"com.totalcross." + targetTCZ + ".fileprovider\""); + int versionCode = Utils.version2int(newVersion); + originalManifest = originalManifest.replaceFirst("(.*\"[vV]ersionCode\"\\R.*\")[0-9]+(\"\\R)((?:.*\\R)*?)(.*int_decimal_value: )[0-9]+", "$1" + versionCode + "$2$3$4" + versionCode); + originalManifest = originalManifest.replaceAll("totalcross\\.android", "totalcross." + targetTCZ); + if (!Deployer4Android.permissionManageExternalStorage){ + originalManifest = originalManifest.replaceFirst("(child\\s*\\{\\s*)(element\\s*\\{\\s*name:\\s*\\\"uses-permission\\\"\\s*.*\\{(?:\\s*.*){3}\\\"android\\.permission\\.MANAGE_EXTERNAL_STORAGE\\\"(?:\\s*.*\\s*\\}){2})", "$1text: \"\\\\n. \""); + } + if (!Deployer4Android.permissionRequestInstallPackages){ + originalManifest = originalManifest.replaceFirst("(child\\s*\\{\\s*)(element\\s*\\{\\s*name:\\s*\\\"uses-permission\\\"\\s*.*\\{(?:\\s*.*){3}\\\"android\\.permission\\.REQUEST_INSTALL_PACKAGES\\\"(?:\\s*.*\\s*\\}){2})", "$1text: \"\\\\n. \""); } - System.out.println(message); - return " (error on installl)"; - } - - private void createLauncher() throws Exception { - String jarIn = Utils.findPath(DeploySettings.etcDir + "launchers/android/Launcher.jar", false); - if (jarIn == null) { - throw new DeployerException("File android/Launcher.jar not found!"); + + + if (!DeploySettings.quiet) { + IOUtils.write(originalManifest, new FileOutputStream(new File(targetDir, "AndroidManifest.xml"))); + } + try (FileOutputStream fos = new FileOutputStream(decodedManifestFile)) { + IOUtils.write(originalManifest, fos); } - jarOut = targetDir + fileName + ".jar"; - ZipFile zipf = new ZipFile(jarIn); - ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(jarOut)); + // 7. encode the updated AndroidManifest + // $PROTO_BIN --encode=aapt.pb.XmlNode --proto_path=tools Configuration.proto Resources.proto < AndroidManifest_temp.xml > $DEST_FOLDER/base/manifest/AndroidManifest.xml + String[] encodeCmd = { protocExecutable, "--encode=aapt.pb.XmlNode", "--proto_path=tools", "Configuration.proto", "Resources.proto" }; + Process encodeProcess = Runtime.getRuntime().exec(encodeCmd, null, new File(DeploySettings.etcDir, "tools/android")); + IOUtils.copy(new FileInputStream(decodedManifestFile), encodeProcess.getOutputStream()); + encodeProcess.getOutputStream().close(); + b.input(encodeProcess.getInputStream()); - for (ZipEntry ze : zipf) { - String name = convertName(ze.getName()); - if (DEBUG) { - System.out.println("=== Entry: " + name); + // 8. replace icons + TFile res = new TFile(targetZip, "base/res/"); + TFile[] drawableFolders = res.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.startsWith("drawable"); } - InputStream zis = zipf.getInputStream(ze); - zos.putNextEntry(new ZipEntry(name)); - if (name.endsWith(".class")) { - convertConstantPool(zis, zos); + }); + + for (TFile tFile : drawableFolders) { + TFile[] iconFiles = tFile.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File dir, String name) { + return name.endsWith("icon.png"); + } + + }); + for (TFile iconFile : iconFiles) { + try (PipedOutputStream pOut = outputToTFile(iconFile)) { + insertIconPng(pOut, iconFile.getName()); + } } - zos.closeEntry(); - zis.close(); } - zipf.close(); - zos.close(); - } + // 9. replace tcfiles.zip + TFile tcFilesZip = new TFile(targetZip, "base/assets/tcfiles.zip").toNonArchiveFile(); + try (PipedOutputStream pOut = outputToTFile(tcFilesZip)) { + insertTCFilesZip(pOut); + } - private void jar2dex() throws Exception { - // java -classpath P:\TotalCross3\etc\tools\android\dx.jar com.android.dx.command.Main --dex --output=classes.dex UIGadgets.jar - String dxjar = Utils.findPath(DeploySettings.etcDir + "tools/android/dx.jar", false); - if (dxjar == null) { - throw new DeployerException("File android/dx.jar not found!"); - } - String javaExe = Utils.searchIn(DeploySettings.path, DeploySettings.appendDotExe("java")); - String[] cmd = { javaExe, "-classpath", DeploySettings.pathAddQuotes(dxjar), "com.android.dx.command.Main", "--dex", - "--output=classes.dex", new File(jarOut).getAbsolutePath() }; // guich@tc124_3: use the absolute path for the file - String out = Utils.exec(cmd, targetDir); - if (!new File(targetDir + "classes.dex").exists()) { - throw new DeployerException( - "An error occured when compiling the Java class with the Dalvik compiler. The command executed was: '" - + Utils.toString(cmd) + "' at the folder '" + targetDir + "'\nThe output of the command is " + out); - } - new File(jarOut).delete(); // delete the jar - } + // 10. update dex + TFile dex = new TFile(targetZip, "base/dex/"); + TFile[] dexFiles = dex.listFiles(new FilenameFilter() { - private void updateResources() throws Exception { - String ap = Utils.findPath(DeploySettings.etcDir + "launchers/android/resources.ap_", false); - if (ap == null) { - throw new DeployerException("File android/resources.ap_ not found!"); - } - String apk = targetDir + fileName + ".apk"; - ZipFile inf = new ZipFile(ap); - ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(apk)); - - // search input zip file, convert and write each entry to output zip file - for (ZipEntry ze : inf) { - ZipEntry ze2; - String name = ze.getName(); - - // keep all the metadata if possible - if (ze.getMethod() != ZipEntry.STORED) { - ze2 = ze; - // little trick to make the entry reusable - ze2.setCompressedSize(-1); - } else { - // the trick above doesn't work with stored entries - // we'll ignore the metadata and use only the name - ze2 = new ZipEntry(ze.getName()); + @Override + public boolean accept(File dir, String name) { + return name.matches("classes[0-9]*\\.dex"); } - InputStream zis = inf.getInputStream(ze2); - if (name.indexOf("tcfiles.zip") >= 0) { - continue; // skip tcfiles from resources - } else if (name.indexOf("resources.arsc") >= 0) { - zos.putNextEntry(ze2); - insertResources_arsc(zis, zos); - } else if (name.indexOf("icon.png") >= 0) { - zos.putNextEntry(ze2); - insertIcon_png(zos, name); - } else if (name.indexOf("AndroidManifest.xml") >= 0) { - zos.putNextEntry(ze2); - insertAndroidManifest_xml(zis, zos); - } else if (singleApk) { - byte[] bytes = Utils.readJavaInputStream(zis); - // for zxing beep.ogg file - if (name.endsWith(".ogg")) { - setEntryAsStored(ze2, bytes); - } - zos.putNextEntry(ze2); - zos.write(bytes, 0, bytes.length); - } - zos.closeEntry(); - zis.close(); - } - ZipEntry ze2 = new ZipEntry("assets/tcfiles.zip"); - insertTCFiles_zip(ze2, zos); - zos.closeEntry(); - - if (singleApk) { - processClassesDexes(tcFolder + "TotalCross.apk", zos); - copyZipEntries(tcFolder + "TotalCross.apk", "res", zos); - copyZipEntries(tcFolder + "TotalCross.apk", "lib", zos); - } else { - // add classes.dex - zos.putNextEntry(new ZipEntry("classes.dex")); - totalcross.io.File f = new totalcross.io.File(targetDir + "classes.dex", totalcross.io.File.READ_WRITE); - int n; - while ((n = f.readBytes(buf, 0, buf.length)) > 0) { - zos.write(buf, 0, n); + }); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (TFile dexFile : dexFiles) { + dexFile.output(baos); + byte[] bytes = baos.toByteArray(); + + try (PipedOutputStream pOut = outputToTFile(dexFile)) { + processClassesDex(bytes, pOut, sourcePackage.getBytes(), targetPackage.getBytes()); } - zos.closeEntry(); - f.delete(); // delete original file } + + // 10. insert google-services.json try { - String google_services_json_path = Utils.findPath("google-services.json", true); + String googleServicesJsonPath = Utils.findPath("google-services.json", true); - if (google_services_json_path == null) { + if (googleServicesJsonPath == null) { throw new FileNotFoundException("can't find google-services.json in TotalCross deploy path"); } - File google_services_json_file = new File(Utils.findPath("google-services.json", true)); - - FileInputStream jsonStream = new FileInputStream(google_services_json_file); - zos.putNextEntry(new ZipEntry("assets/google-services.json")); - IOUtils.copy(jsonStream, zos); - zos.closeEntry(); + File googleServicesJsonFile = new File(Utils.findPath("google-services.json", true)); - jsonStream.close(); + try (FileInputStream jsonStream = new FileInputStream(googleServicesJsonFile)){ + TFile googleServicesJson = new TFile(targetZip, "base/assets/google-services.json"); + googleServicesJson.input(jsonStream); + } } catch (FileNotFoundException e) { System.out .println("Could not find 'google-services.json', thus Firebase will be ignored further on (Android deploy)"); } - zos.close(); - inf.close(); - } + // 11. unmount to commit changes + TVFS.umount(targetZip); + + // 12. copy app file to target folder + final File rawAabFile = File.createTempFile(fileName, ".aab"); + rawAabFile.deleteOnExit(); + FileUtils.copyFile(targetFile, rawAabFile); - private void processClassesDexes(String baseApk, ZipOutputStream zos) throws Exception { - ZipFile zipf = new ZipFile(baseApk); + // 13. use jarsigner to sign the app + // jarsigner -verbose -keystore $ANDROID_SIGNING_KEY -storepass $SIGN_KEY_PWD -keypass $KEY_PASS $TEMP_FILE $KEY_ALIAS + if (Boolean.parseBoolean(signingConfig.getProperty("aab_signing_enabled"))) { + Utils.jarSigner(rawAabFile.getName(), rawAabFile.getParent(), (Properties) signingConfig.clone()); - for (ZipEntry entry : zipf) { - if (entry.getName().matches("classes[0-9]*\\.dex")) { - processClassesDex(tcFolder + "TotalCross.apk", entry, zos); + // 14. zipalign the app + // $ZIP_ALIGN -f -v -p 4 $TEMP_FILE $OUT_FILE + new ZipAlign().zipAlign(rawAabFile, new File(targetDir, fileName + ".aab")); + } else { + FileUtils.copyFile(rawAabFile, new File(targetDir, fileName + ".aab")); + } + + // 15. generate apk + // java -Djava.io.tmpdir=$PWD -jar $BUNDLE_TOOL build-apks --bundle $OUT_FILE --mode universal --output rollback.apks --output-format=DIRECTORY --ks $ANDROID_SIGNING_KEY --ks-pass "pass:$SIGN_KEY_PWD" --ks-key-alias "$KEY_ALIAS" --key-pass "pass:$KEY_PASS" + List javaCmdList = new ArrayList<>(); + + final String javaExe = Utils.searchIn(DeploySettings.path, DeploySettings.appendDotExe("java")); + final String bundletoolJar = BundletoolExec.getJarPath(); + javaCmdList.add(javaExe); + javaCmdList.add("-Djava.io.tmpdir=" + new File(targetDir).getAbsolutePath()); + javaCmdList.add("-jar"); + javaCmdList.add(bundletoolJar); + javaCmdList.add("build-apks"); + javaCmdList.add("--bundle=" + fileName + ".aab"); + javaCmdList.add("--mode=universal"); + javaCmdList.add("--output=" + fileName + ".apks"); + javaCmdList.add("--output-format=DIRECTORY"); + + if (Boolean.parseBoolean(signingConfig.getProperty("apk_signing_enabled"))) { + String keystore = Utils.findPath(signingConfig.getProperty("apk_keystore_path"), false); + if (keystore == null) { + keystore = Utils.findPath(DeploySettings.homeDir + signingConfig.getProperty("apk_keystore_path"), false); + if (keystore == null) { + throw new DeployerException("Keystore for APK signing not found!"); + } } + javaCmdList.add("--ks=" + new File(keystore).getAbsolutePath()); + javaCmdList.add("--ks-pass=pass:" + signingConfig.getProperty("apk_keystore_storepass")); + javaCmdList.add("--ks-key-alias=" + signingConfig.getProperty("apk_keystore_alias")); + javaCmdList.add("--key-pass=pass:" + signingConfig.getProperty("apk_keystore_keypass")); + } + + String execOutput = Utils.exec(javaCmdList.toArray(new String[0]), new File(targetDir).getAbsolutePath()); + if (!DeploySettings.quiet) { + System.out.println(execOutput); } - zipf.close(); + FileUtils.moveFile(new File(targetDir, fileName + ".apks/universal.apk"), new File(targetDir, fileName + ".apk")); + FileUtils.deleteQuietly(new File(targetDir, fileName + ".apks")); + + String apk = targetDir + "/" + fileName + ".apk"; + + String extraMsg = ""; + if (DeploySettings.installPlatforms.indexOf("android,") >= 0) { + extraMsg = callADB(apk); + } + + System.out.println("... Files written to folder " + targetDir + extraMsg); } - private void copyZipEntries(String srcZip, String initPath, ZipOutputStream zos) throws IOException { - ZipFile zipf = new ZipFile(srcZip); - for (ZipEntry zEntry : zipf) { - String zentryName = zEntry.getName(); - InputStream zIn = zipf.getInputStream(zEntry); - if (zentryName.endsWith("/") || zentryName.endsWith("\\") || zentryName.indexOf("icon.png") >= 0) { - // it's a directory or an old icon, just continue - continue; - } - if (zentryName.startsWith(initPath)) { - byte[] bytes = Utils.readJavaInputStream(zIn); - copyEntryBytes(zEntry, bytes, zos); - } - zIn.close(); + private String callADB(String apk) throws Exception { + String adb = Utils.findPath(DeploySettings.etcDir + "tools/android/adb.exe", false); + if (adb == null) { + throw new DeployerException("File android/adb.exe not found!"); + } + String message = Utils.exec(new String[] { adb, "install", "-r", apk }, targetDir); + if (message != null && message.indexOf("INPUT:Success") >= 0) { + return " (installed)"; } - zipf.close(); + System.out.println(message); + return " (error on installl)"; } // http://strazzere.com/blog/?p=3 @@ -351,7 +543,7 @@ private static void calcSignature(byte bytes[]) { int amt = md.digest(bytes, 12, 20); if (amt != 20) { throw new RuntimeException( - (new StringBuilder()).append("unexpected digest write:").append(amt).append("bytes").toString()); + new StringBuilder().append("unexpected digest write:").append(amt).append("bytes").toString()); } } catch (java.security.DigestException ex) { throw new RuntimeException(ex); @@ -368,12 +560,8 @@ private static void calcChecksum(byte bytes[]) { bytes[11] = (byte) (sum >> 24); } - private void processClassesDex(String srcZip, ZipEntry dexEntry, ZipOutputStream dstZip) throws Exception { - String fileName = dexEntry.getName(); - dstZip.putNextEntry(new ZipEntry(fileName)); - byte[] bytes = Utils.loadZipEntry(srcZip, fileName); - - replaceBytes(bytes, sourcePackage.getBytes(), targetPackage.getBytes()); + private static void processClassesDex(byte[] bytes, OutputStream outputStream, byte[] sourcePackageBytes, byte[] targetPackageBytes) throws Exception { + replaceBytes(bytes, sourcePackageBytes, targetPackageBytes); if (DeploySettings.autoStart || DeploySettings.isService) { System.out.println("Is service."); replaceBytes(bytes, new byte[] { (byte) 0x71, (byte) 0xC3, (byte) 0x5B, (byte) 0x07 }, @@ -381,38 +569,18 @@ private void processClassesDex(String srcZip, ZipEntry dexEntry, ZipOutputStream } calcSignature(bytes); calcChecksum(bytes); - dstZip.write(bytes, 0, bytes.length); - dstZip.closeEntry(); + outputStream.write(bytes, 0, bytes.length); } - private void replaceBytes(byte[] bytes, byte[] fromBytes, byte[] toBytes) { + private static void replaceBytes(byte[] bytes, byte[] fromBytes, byte[] toBytes) { int ofs = 0; while ((ofs = Utils.indexOf(bytes, fromBytes, false, ofs)) != -1) { - totalcross.sys.Vm.arrayCopy(toBytes, 0, bytes, ofs, toBytes.length); + Vm.arrayCopy(toBytes, 0, bytes, ofs, toBytes.length); ofs += toBytes.length; } } - private void copyZipEntry(String srcZip, String fileName, ZipOutputStream dstZip) throws Exception { - byte[] bytes = Utils.loadZipEntry(srcZip, fileName); - ZipEntry ze = new ZipEntry(fileName); - - copyEntryBytes(ze, bytes, dstZip); - dstZip.closeEntry(); - } - - private void copyEntryBytes(ZipEntry ze, byte[] bytes, ZipOutputStream dstZip) throws IOException { - String zentryName = ze.getName(); - if (zentryName.endsWith(".ogg")) // for zxing beep.ogg file - { - setEntryAsStored(ze, bytes); - } - - dstZip.putNextEntry(ze); - dstZip.write(bytes, 0, bytes.length); - } - - private void insertIcon_png(ZipOutputStream zos, String name) throws Exception { + private void insertIconPng(OutputStream zos, String name) throws Exception { if (DeploySettings.bitmaps != null) { int res; if (name.startsWith("res/drawable-xhdpi") && name.endsWith("icon.png")) { @@ -428,238 +596,7 @@ private void insertIcon_png(ZipOutputStream zos, String name) throws Exception { } } - private totalcross.io.ByteArrayStream readInputStream(java.io.InputStream is) { - totalcross.io.ByteArrayStream bas = new totalcross.io.ByteArrayStream(2048); - int len; - while (true) { - try { - len = is.read(buf); - } catch (java.io.IOException e) { - break; - } - if (len > 0) { - bas.writeBytes(buf, 0, len); - } else { - break; - } - } - return bas; - } - - private void insertAndroidManifest_xml(InputStream zis, OutputStream zos) throws Exception { - totalcross.io.ByteArrayStream bas; - if (includeSms) { - byte[] bytes = Utils.loadFile(DeploySettings.etcDir + "tools/android/AndroidManifest_includeSms.xml", true); - bas = new totalcross.io.ByteArrayStream(bytes); - bas.skipBytes(bytes.length); - } else if (singleApk) { - byte[] bytes = Utils.loadFile(DeploySettings.etcDir + "tools/android/AndroidManifest_singleApk.xml", true); - bas = new totalcross.io.ByteArrayStream(bytes); - bas.skipBytes(bytes.length); - } else { - bas = readInputStream(zis); - } - bas.mark(); - totalcross.io.DataStreamLE ds = new totalcross.io.DataStreamLE(bas); - String oldPackage, oldTitle, oldActivity; - - if (singleApk) { - oldPackage = sourcePackage.replace('/', '.'); - oldTitle = "Stub"; - oldActivity = null; - } else { - oldPackage = "totalcross.app.stub"; - oldTitle = "Stub"; - oldActivity = ".Stub"; - } - - String oldVersion = "!1.0!"; - String oldSharedId = singleApk ? "totalcross.app.sharedid" : null; - - String newPackage = targetPackage.replace('/', '.'); - String newVersion = DeploySettings.appVersion != null ? DeploySettings.appVersion : "1.0"; - String newTitle = DeploySettings.appTitle; - String newActivity = singleApk ? null : "." + fileName; - String newSharedId = singleApk ? "totalcross.app.app" + DeploySettings.applicationId.toLowerCase() : null; - - int oldSize = bas.available(); - int difPackage = (newPackage.length() - oldPackage.length()) * 2; - int difVersion = (newVersion.length() - oldVersion.length()) * 2; - int difTitle = (newTitle.length() - oldTitle.length()) * 2; - int difActivity = singleApk ? 0 : (newActivity.length() - oldActivity.length()) * 2; - int difSharedId = !singleApk ? 0 : (newSharedId.length() - oldSharedId.length()) * 2; - int dif = difPackage + difVersion + difTitle + difActivity + difSharedId; - String newTcPackage = "totalcross.and" + DeploySettings.applicationId.toLowerCase(); // totalcross.android -> totalcross.app.tctestwin - - // get the xml size - bas.setPos(12); - int xmlsize = ds.readInt(); - xmlsize += dif; - int gap = ((xmlsize + 3) & ~3) - xmlsize; - - int newSize = oldSize + dif + gap; - - // update new size and position of - bas.setPos(4); - ds.writeInt(newSize); - bas.setPos(12); - ds.writeInt(xmlsize + gap); - int len = ds.readInt(); - - bas.setPos(40); - int[] positions = new int[len + 1]; - for (int i = 0, last = len - 1; i <= last; i++) { - if (i < last) { - positions[i + 1] = ds.readInt(); - } - if (DEBUG) { - System.out.println(i + " " + positions[i] + " (" + (positions[i + 1] - positions[i]) + ")"); - } - } - - String[] strings = new String[len]; - int pos0 = bas.getPos(); - for (int i = 0; i < len; i++) { - int pos = bas.getPos(); - String s = new String(ds.readChars()); - if (DEBUG) { - System.out.println(i + " #" + pos + " (" + (pos - pos0) + ") " + s + " (" + s.length() + " - " - + (s.length() * 2 + 2 + 2) + ")"); - } - strings[i] = s; - bas.skipBytes(2); // skip 0-terminated string - } - - // read the rest of the resource (other kinds of data) - int resSize = bas.available(); - byte[] res = new byte[resSize]; - bas.readBytes(res, 0, resSize); - int ofs; - - // now the "versionCode" is used to store some properties of the application - // search and replace the value of versionCode="305419896" (0x12345678) with the application properties - // note that currently there's no application properties! - byte[] versionCodeMark = { (byte) 0x78, (byte) 0x56, (byte) 0x34, (byte) 0x12 }; - ofs = Utils.indexOf(res, versionCodeMark, false); - if (ofs == -1) { - throw new DeployerException("Error: could not find position for versionCode"); - } - totalcross.io.ByteArrayStream dtbas = new totalcross.io.ByteArrayStream(res); - dtbas.setPos(ofs); - totalcross.io.DataStreamLE dsbas = new totalcross.io.DataStreamLE(dtbas); - int props = Utils.version2int(newVersion); - dsbas.writeInt(props); - - boolean isFullScreen = DeploySettings.isFullScreenPlatform(totalcross.sys.Settings.ANDROID); // guich@tc120_59 - // now, change the names accordingly - for (int i = 0; i < len; i++) { - String s = strings[i]; - if (isFullScreen && s.equals("fullscreen:0")) { - strings[i] = "fullscreen:1"; - } else if (s.startsWith(oldPackage)) { - if (singleApk) { - strings[i] = newPackage + s.substring(oldPackage.length()); - } else if (s.endsWith("google_measurement_service")) { - strings[i] = newTcPackage + s.substring(oldPackage.length()); - } - } else if ("com.totalcross.android.fileprovider".equals(s)) { - strings[i] = s.replace("android", targetTCZ); - } - if (DeploySettings.allowBackup == false) { - /* - * To deactivate allowBackup we swap the properties around. - * - * allowBackup starts with true and changes to false. - * backupInForeground starts with false and changes to true. - */ - if (s.equals("allowBackup")) { - strings[i] = "backupInForeground"; - } else if (s.equals("backupInForeground")) { - strings[i] = "allowBackup"; - } - } - if (oldPackage != null && s.equals(oldPackage)) { - strings[i] = newPackage; - } else if (oldVersion != null && s.equals(oldVersion)) { - strings[i] = newVersion; - } else if (oldTitle != null && s.equals(oldTitle)) { - strings[i] = newTitle; - } else if (oldActivity != null && s.equals(oldActivity)) { - strings[i] = newActivity; - } else if (oldSharedId != null && s.equals(oldSharedId)) { - strings[i] = newSharedId; - } - } - // update the offsets table - for (int i = 0; i < len; i++) { - positions[i + 1] = positions[i] + (strings[i].length() * 2 + 2 + 2); // 2 for the positions, and 2 for the 0 termination - } - - // now write everything again - bas.setPos(36); - for (int i = 0; i < len; i++) { - ds.writeInt(positions[i]); - } - for (int i = 0; i < len; i++) { - String s = strings[i]; - ds.writeChars(s, s.length()); - ds.writeShort(0); - } - if (gap > 0) { - for (int i = 0; i < gap; i++) { - ds.writeByte(0); - } - } - - ds.writeBytes(res); - int nn = bas.getPos(); - if (nn != newSize) { - throw new DeployerException( - "Something went wrong when parsing AndroidManifest.xml. Expected size is " + newSize + ", but got " + nn); - } - - zos.write(bas.getBuffer(), 0, newSize); - } - - private void insertResources_arsc(InputStream zis, OutputStream zos) throws Exception { - byte[] all; - byte[] key; - if (includeSms) { - key = new byte[] { 't', (byte) 0, 'o', (byte) 0, 't', (byte) 0, 'a', (byte) 0, 'l', (byte) 0, 'c', (byte) 0, 'r', - (byte) 0, 'o', (byte) 0, 's', (byte) 0, 's', (byte) 0, '.', (byte) 0, 'a', (byte) 0, 'n', (byte) 0, 'd', - (byte) 0, 'r', (byte) 0, 'o', (byte) 0, 'i', (byte) 0, 'd', (byte) 0 }; - all = Utils.loadFile(DeploySettings.etcDir + "tools/android/resources_includeSms.arsc", true); - } else if (singleApk) { - key = new byte[] { 't', (byte) 0, 'o', (byte) 0, 't', (byte) 0, 'a', (byte) 0, 'l', (byte) 0, 'c', (byte) 0, 'r', - (byte) 0, 'o', (byte) 0, 's', (byte) 0, 's', (byte) 0, '.', (byte) 0, 'a', (byte) 0, 'n', (byte) 0, 'd', - (byte) 0, 'r', (byte) 0, 'o', (byte) 0, 'i', (byte) 0, 'd', (byte) 0 }; - all = Utils.loadFile(DeploySettings.etcDir + "tools/android/resources_singleApk.arsc", true); - } else { - key = new byte[] { 't', (byte) 0, 'o', (byte) 0, 't', (byte) 0, 'a', (byte) 0, 'l', (byte) 0, 'c', (byte) 0, 'r', - (byte) 0, 'o', (byte) 0, 's', (byte) 0, 's', (byte) 0, '.', (byte) 0, 'a', (byte) 0, 'p', (byte) 0, 'p', - (byte) 0, '.', (byte) 0, 's', (byte) 0, 't', (byte) 0, 'u', (byte) 0, 'b', (byte) 0 }; - all = readInputStream(zis).toByteArray(); - } - int ofs = Utils.indexOf(all, key, false); - if (ofs == -1) { - throw new DeployerException("Could not find position for totalcross.android in arsc."); - } - // write the name - char[] chars = targetPackage.replace('/', '.').toCharArray(); - if (chars.length > 0x7F) { - throw new DeployerException("The package name length can't be bigger than " + 0x7F); - } - int i = 0, n = ofs + 0x7F * 2; - for (; i < chars.length; i++, ofs += 2) { - all[ofs] = (byte) chars[i]; - } - while (ofs < n) { - all[ofs++] = (byte) 0; - } - zos.write(all); - } - - private void insertTCFiles_zip(ZipEntry ze, ZipOutputStream z) throws Exception { + private void insertTCFilesZip(OutputStream z) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(8192); // parse the android.pkg @@ -678,24 +615,18 @@ private void insertTCFiles_zip(ZipEntry ze, ZipOutputStream z) throws Exception if (vGlobals.size() > 0) { vLocals.addElements(vGlobals.toObjectArray()); } - if (singleApk) // include the vm? - { - // tc is always included - // include non-binary files - List defaultTczs = DeploySettings.getDefaultTczs(); - for (File file : defaultTczs) { - vLocals.addElement(file.getAbsolutePath()); - } + // tc is always included + // include non-binary files + List defaultTczs = DeploySettings.getDefaultTczs(); + for (File file : defaultTczs) { + vLocals.addElement(file.getAbsolutePath()); } Utils.preprocessPKG(vLocals, true); writeVlocals(baos, vector2set(vLocals, new HashSet())); // add the file UNCOMPRESSED - byte[] bytes = baos.toByteArray(); - setEntryAsStored(ze, bytes); - z.putNextEntry(ze); - z.write(bytes); + baos.writeTo(z); } public static Set vector2set(Vector vec, Set set) { @@ -710,11 +641,11 @@ public static Set vector2set(Vector vec, Set set) { private void writeVlocals(ByteArrayOutputStream baos, Set vLocals) throws IOException { ZipOutputStream zos = new ZipOutputStream(baos); for (String item : vLocals) { - String[] pathnames = totalcross.sys.Convert.tokenizeString(item, ','); + String[] pathnames = Convert.tokenizeString(item, ','); String pathname = pathnames[0]; String name = Utils.getFileName(pathname); if (pathnames.length > 1) { - name = totalcross.sys.Convert.appendPath(pathnames[1], name); + name = Convert.appendPath(pathnames[1], name); if (name.startsWith("/")) { name = name.substring(1); } @@ -723,43 +654,39 @@ private void writeVlocals(ByteArrayOutputStream baos, Set vLocals) throw if (tcFolder != null && pathname.equals(DeploySettings.tczFileName)) { name = targetTCZ + ".tcz"; } - FileInputStream fis; - try { - fis = new FileInputStream(pathname); - } catch (FileNotFoundException fnfe) { - try { - fis = new FileInputStream(totalcross.sys.Convert.appendPath(DeploySettings.currentDir, pathname)); - } catch (FileNotFoundException fnfe2) { - String pp = Utils.findPath(pathname, true); - if (pp != null) { - fis = new FileInputStream(pp); - } else { - System.out.println("File not found: " + pathname); - continue; - } - } + + File f = new File(pathname); + if (!f.exists() || !f.isFile() || !f.canRead()) { + f = new File(Convert.appendPath(DeploySettings.currentDir, pathname)); } - int avaiable = fis.available(); - - ByteArrayOutputStream secondary = new ByteArrayOutputStream(avaiable); - IOUtils.copy(fis, secondary); - byte[] bytes = secondary.toByteArray(); - fis.close(); - ZipEntry zze = new ZipEntry(name); - // tcz files will be stored without - // compression so they can be read - // directly - if (name.endsWith(".tcz")) { - setEntryAsStored(zze, bytes); + + File file = getFirstReadableFile(pathname, Convert.appendPath(DeploySettings.currentDir, pathname), Utils.findPath(pathname, true)); + if (file == null) { + System.out.println("File not found: " + pathname); + continue; + } + + try (FileInputStream fis = new FileInputStream(file)){ + int avaiable = fis.available(); + + ByteArrayOutputStream secondary = new ByteArrayOutputStream(avaiable); + IOUtils.copy(fis, secondary); + byte[] bytes = secondary.toByteArray(); + fis.close(); + ZipEntry zze = new ZipEntry(name); + // tcz files will be stored without compression so they can be read directly + if (name.endsWith(".tcz")) { + setEntryAsStored(zze, bytes); + } + zos.putNextEntry(zze); + zos.write(bytes); + zos.closeEntry(); } - zos.putNextEntry(zze); - zos.write(bytes); - zos.closeEntry(); } zos.close(); } - private void setEntryAsStored(ZipEntry entry, byte[] content) { + private static void setEntryAsStored(ZipEntry entry, byte[] content) { CRC32 crc = new CRC32(); crc.update(content); entry.setCrc(crc.getValue()); @@ -768,140 +695,33 @@ private void setEntryAsStored(ZipEntry entry, byte[] content) { entry.setSize(content.length); } - private void convertConstantPool(InputStream is, ZipOutputStream os) throws Exception { - totalcross.io.ByteArrayStream bas = new totalcross.io.ByteArrayStream(1024); - totalcross.io.DataStream ds = new totalcross.io.DataStream(bas); - int n; - - while ((n = is.read(buf)) > 0) { - bas.writeBytes(buf, 0, n); - } - bas.setPos(0); - JavaClass jclass = new JavaClass(); - jclass.load(ds); - - checkConstantPool(jclass); - - bas.reuse(); - jclass.save(ds); - os.write(bas.getBuffer(), 0, bas.getPos()); - } - - private static void checkConstantPool(JavaClass jclass) { - UTF8 descriptor; - // Check all class and name/type constants - if (DEBUG) { - System.out.println("Constant pool"); - } - int count = jclass.constantPool.size(); - for (int i = 1; i < count; i++) { - JavaConstant constant = (JavaConstant) jclass.constantPool.items[i]; - i += constant.slots() - 1; // skip empty slots - - switch (constant.tag) { - case JavaConstant.CONSTANT_INTEGER: - String cla = jclass.getClassName(); - if ((DeploySettings.autoStart || DeploySettings.isService) && cla.endsWith("/StartupIntentReceiver") - && ((Integer) constant.info).value == 123454321) { - Integer it = new Integer(); - it.value = DeploySettings.isService ? 1 : 0; - constant.info = it; + private static File getFirstReadableFile (String... paths) { + for (String path : paths) { + if (path != null) { + File f = new File(path); + if (f.exists() && f.isFile() && f.canRead()) { + return f; } - break; - case JavaConstant.CONSTANT_CLASS: - descriptor = ((Class) constant.info).getValueAsName(); - descriptor.value = convertName(descriptor.value); - break; - case JavaConstant.CONSTANT_NAME_AND_TYPE: - descriptor = ((NameAndType) constant.info).getValue2AsDescriptor(); - descriptor.value = convertName(descriptor.value); - break; - } - } - - // Check class fields - if (DEBUG) { - System.out.println("Fields"); - } - count = jclass.fields.size(); - for (int i = 0; i < count; i++) { - JavaField field = (JavaField) jclass.fields.items[i]; - - descriptor = (UTF8) field.descriptor.info; - descriptor.value = convertName(descriptor.value); - - // Check field attributes - int count2 = field.attributes.size(); - for (int j = 0; j < count2; j++) { - checkAttribute((JavaAttribute) field.attributes.items[j]); - } - } - - // Check class methods - if (DEBUG) { - System.out.println("Methods"); - } - count = jclass.methods.size(); - for (int i = 0; i < count; i++) { - JavaMethod method = (JavaMethod) jclass.methods.items[i]; - - descriptor = (UTF8) method.descriptor.info; - descriptor.value = convertName(descriptor.value); - - // Check method attributes - int count2 = method.attributes.size(); - for (int j = 0; j < count2; j++) { - checkAttribute((JavaAttribute) method.attributes.items[j]); } } - - // Check class attributes - if (DEBUG) { - System.out.println("Atributes"); - } - count = jclass.attributes.size(); - for (int i = 0; i < count; i++) { - checkAttribute((JavaAttribute) jclass.attributes.items[i]); - } - - if (DEBUG) { - System.out.println("FINISHED"); - } + return null; } - /** - * @param attribute - * @param classes - */ - private static void checkAttribute(JavaAttribute attribute) { - if (attribute.info instanceof SourceFile) { - JavaConstant source = ((SourceFile) attribute.info).sourceFile; - UTF8 descriptor = (UTF8) source.info; - descriptor.value = convertName(descriptor.value); - } else if (attribute.info instanceof LocalVariableTable) { - Vector variables = ((LocalVariableTable) attribute.info).variables; - int count = variables.size(); - for (int i = 0; i < count; i++) { - UTF8 descriptor = (UTF8) ((LocalVariableTable.LocalVariable) variables.items[i]).descriptor.info; - descriptor.value = convertName(descriptor.value); - } - } else if (attribute.info instanceof Code) { - Code code = (Code) attribute.info; - Vector attributes = code.attributes; - int count = attributes.size(); - for (int i = 0; i < count; i++) { - checkAttribute((JavaAttribute) attributes.items[i]); - } - } - } + private static PipedOutputStream outputToTFile(TFile file) throws IOException { + final PipedOutputStream pOut = new PipedOutputStream(); + final PipedInputStream pIn = new PipedInputStream(pOut); + + new Thread(new Runnable() { - private static String convertName(String name) { - String value = name.replace("totalcross/app/stub", targetPackage); // totalcross/app/stub/R -> totalcross/app/uigadgets/R - value = value.replace("Stub", fileName); // totalcross/app/stub/Stub -> totalcross/app/uigadgets/Stub - if (DEBUG) { - System.out.println(name + " -> " + value); - } - return value; + @Override + public void run() { + try { + file.input(pIn); + } catch (Exception e) { + e.printStackTrace(); + } + } + }).start(); + return pOut; } - } diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4WinCE.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4WinCE.java index 8b99e55106..bdcec64924 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4WinCE.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/Deployer4WinCE.java @@ -228,7 +228,7 @@ public void createInstallFiles() throws Exception { vLocals.addElements(DeploySettings.tczs); Utils.preprocessPKG(vLocals, false); Utils.preprocessPKG(vGlobals, false); - String tcFolder = null, lbFolder = null; + String tcFolder = null; if (DeploySettings.packageVM) // include the vm? { if (!hasExe) { @@ -240,7 +240,6 @@ public void createInstallFiles() throws Exception { for (java.io.File file : defaultTczs) { vLocals.addElement(file.getAbsolutePath()); } - lbFolder = Convert.appendPath(DeploySettings.folderTotalCross3DistVM, "wince"); tcFolder = Convert.appendPath(DeploySettings.folderTotalCross3DistVM, "wince"); // copy binary files try { @@ -249,10 +248,6 @@ public void createInstallFiles() throws Exception { } String name = "/TCVM.dll"; File.copy(Convert.appendPath(tcFolder, name), Convert.appendPath(targetDir, name)); - if (lbFolder != null) { - name = "/Litebase.dll"; - File.copy(Convert.appendPath(lbFolder, name), Convert.appendPath(targetDir, name)); - } } } @@ -310,7 +305,7 @@ public void createInstallFiles() throws Exception { "[SourceDisksFiles]\n" + (hasExe ? (DeploySettings.filePrefix + ".exe = 1\n") : "") - + (tcFolder != null ? ("TCVM.dll = 1\n") : "") + (lbFolder != null ? ("Litebase.dll = 1\n") : "") + + (tcFolder != null ? ("TCVM.dll = 1\n") : "") + toString(vLocals, " = 2\n", false) + toString(vGlobals, " = 3\n", false) + //----------------------------------------------- @@ -322,7 +317,7 @@ public void createInstallFiles() throws Exception { + "LocalFiles = 0,%InstallDir%\n" + "Startmenu = 0,%CE11%\n" + (hasExe ? ("[Binaries]\n" + DeploySettings.filePrefix + ".exe\n") : "") - + (tcFolder != null ? ("TCVM.dll\n") : "") + (lbFolder != null ? ("Litebase.dll\n") : "") + + + (tcFolder != null ? ("TCVM.dll\n") : "") + "[LocalFiles]\n" + toString(vLocals, "\n", true) + diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/LinuxBuildNatives.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/LinuxBuildNatives.java index 5cc65a7085..6fcc020a9f 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/LinuxBuildNatives.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/LinuxBuildNatives.java @@ -22,7 +22,7 @@ import totalcross.util.Vector; /** - * Generates Linux packages for TotalCross VM and Litebase. + * Generates Linux packages for TotalCross VM. */ public class LinuxBuildNatives { diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/Utils.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/Utils.java index b3fb1a8386..ef2d995463 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/Utils.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/Utils.java @@ -4,6 +4,8 @@ // SPDX-License-Identifier: LGPL-2.1-only package tc.tools.deployer; +import java.io.FileInputStream; +import java.util.Properties; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -908,6 +910,14 @@ public static void removeQuotes(String[] path) { ///////////////////////////////////////////////////////////////////////////////////// public static void jarSigner(String jar, String targetDir) throws Exception { + Properties config = new Properties(); + try (FileInputStream fis = new FileInputStream(Utils.findPath(DeploySettings.etcDir + "security/android_keystore.properties", false))) { + config.load(fis); + } + jarSigner(jar, targetDir, config); + } + + public static void jarSigner(String jar, String targetDir, Properties config) throws Exception { // Certificate fingerprint (MD5): 0D:79:8E:42:A9:CD:50:AC:29:72:85:F8:12:3C:22:0E // jarsigner -keystore P:\TotalCross3\etc\security\tcandroidkey.keystore -storepass @ndroid$w -keypass @ndroidsw UIGadgets.apk tcandroidkey String jarsignerExe = Utils.searchIn(DeploySettings.path, DeploySettings.appendDotExe("jarsigner")); @@ -918,27 +928,34 @@ public static void jarSigner(String jar, String targetDir) throws Exception { if (jarsignerExe.contains(" ")) { jarsignerExe = DeploySettings.appendDotExe("jarsigner"); } - String keystore = Utils.findPath(DeploySettings.etcDir + "security/tcandroidkey.keystore", false); + String keystore = Utils.findPath(config.getProperty("aab_keystore_path"), false); if (keystore == null) { - throw new DeployerException("File security/tcandroidkey.keystore not found!"); + keystore = Utils.findPath(DeploySettings.homeDir + config.getProperty("aab_keystore_path"), false); + if (keystore == null) { + throw new DeployerException("Keystore for AAB signing not found!"); + } } keystore = new java.io.File(keystore).getAbsolutePath(); Vector v = new Vector(10); v.addElement(jarsignerExe); if (DeploySettings.dJavaVersion >= 1.7) { - v.addElements(new String[] { "-digestalg", "SHA1", "-sigalg", "MD5withRSA" }); + v.addElements(new String[] { + "-digestalg", config.getProperty("aab_keystore_digestalg"), + "-sigalg", config.getProperty("aab_keystore_sigalg") }); } v.addElement("-keystore"); v.addElement(keystore); + v.addElement("-storetype"); + v.addElement(config.getProperty("aab_keystore_storetype")); v.addElement("-storepass"); - v.addElement("@ndroid$w"); + v.addElement(config.getProperty("aab_keystore_storepass")); v.addElement("-keypass"); - v.addElement("@ndroidsw"); + v.addElement(config.getProperty("aab_keystore_keypass")); v.addElement(jar); - v.addElement("tcandroidkey"); + v.addElement(config.getProperty("aab_keystore_alias")); String out = Utils.exec((String[]) v.toObjectArray(), targetDir); if (out != null && !out.startsWith("INPUT:jar signed")) { - throw new DeployerException("An error occured when signing the APK. The output is " + out); + throw new DeployerException("An error occured when signing the AAB. The output is " + out); } } diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryReader.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryReader.java index 1476952ca6..63c5969fc7 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryReader.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryReader.java @@ -27,6 +27,12 @@ public long readUnsignedInt() throws IOException { return ((((long) (b[0] & 0xFF)) << 24) | (((long) (b[1] & 0xFF)) << 16) | ((b[2] & 0xFF) << 8) | (b[3] & 0xFF)); } + public long readUnsignedLong() throws IOException { + long l1 = (long) readUnsignedInt() & 0xFFFFFFFFL; + long l2 = (long) readUnsignedInt() & 0xFFFFFFFFL; + return (l1 << 32) | l2; + } + public long readUnsignedIntLE() throws IOException { byte[] b = new byte[4]; read(b); diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryWriter.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryWriter.java index 04f088fd90..57f30b168f 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryWriter.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/ElephantMemoryWriter.java @@ -39,6 +39,26 @@ public void writeUnsignedInt(long value) throws IOException { this.write(b); } + public void writeUnsignedLong(long value) { + byte[] b = new byte[8]; + b[7] = (byte) value; + value >>= 8; + b[6] = (byte) value; + value >>= 8; + b[5] = (byte) value; + value >>= 8; + b[4] = (byte) value; + value >>= 8; + b[3] = (byte) value; + value >>= 8; + b[2] = (byte) value; + value >>= 8; + b[1] = (byte) value; + value >>= 8; + b[0] = (byte) value; + this.write(b); + } + public void writeUnsignedIntLE(long value) throws IOException { byte[] b = new byte[4]; int i = (int) value; diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/MachObjectFile.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/MachObjectFile.java index 1ab1858217..2e2b3265b8 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/MachObjectFile.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/MachObjectFile.java @@ -18,11 +18,7 @@ import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.x509.X509Store; -import tc.tools.deployer.ipa.blob.BlobWrapper; -import tc.tools.deployer.ipa.blob.CodeDirectory; import tc.tools.deployer.ipa.blob.EmbeddedSignature; -import tc.tools.deployer.ipa.blob.Entitlements; -import tc.tools.deployer.ipa.blob.Requirements; /** * http://llvm.org/docs/doxygen/html/MachOFormat_8h_source.html @@ -103,31 +99,23 @@ public void setEmbeddedSignature(EmbeddedSignature signature) throws IOException public byte[] resign(KeyStore ks, X509Store certStore, String bundleIdentifier, byte[] entitlementsBytes, byte[] info, byte[] sourceData) throws IOException, CMSException, UnrecoverableKeyException, CertificateEncodingException, KeyStoreException, NoSuchAlgorithmException, OperatorCreationException { - // create a new codeDirectory with the new identifier, but keeping the same codeLimit - CodeDirectory codeDirectory = new CodeDirectory(bundleIdentifier, lc_signature.signature.codeDirectory.codeLimit); - // now create brand new entitlements and requirements - Entitlements entitlements = new Entitlements(entitlementsBytes); - Requirements requirements = new Requirements(); + // update the bundle identifier + this.lc_signature.signature.setBundleIdentifier(bundleIdentifier); + + // update the entitlements data + this.lc_signature.signature.entitlements.setData(entitlementsBytes); - // now create the blob wrapper - BlobWrapper blobWrapper = new BlobWrapper(ks, certStore, codeDirectory); + // recalculate slots hashes + this.lc_signature.signature.updateCodeDirectoryHashes(this.data, info, sourceData, null); - // finally create the template of our new signature - EmbeddedSignature newSignature = new EmbeddedSignature(codeDirectory, entitlements, requirements, blobWrapper); - - // add the new signature to the file - this.setEmbeddedSignature(newSignature); - - // recalculate hashes - codeDirectory.setSpecialSlotsHashes(info, requirements.getBytes(), sourceData, null, entitlements.getBytes()); - codeDirectory.setCodeSlotsHashes(this.data); - - lc_signature.signature.sign(); + lc_signature.signature.sign(ks, certStore); byte[] resignedData = lc_signature.signature.getBytes(); +/* + Not sure if still valid, might want to test and maybe fix it later if (signatureTemplate.length != resignedData.length) { throw new IllegalStateException("Failed to resign the file, please try again."); } - + */ ElephantMemoryWriter writer = new ElephantMemoryWriter(data); writer.memorize(); writer.moveTo(lc_signature.blobFileOffset); diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobCore.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobCore.java index 7244bda476..818c19d272 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobCore.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobCore.java @@ -33,6 +33,7 @@ protected void writeToStream(ElephantMemoryWriter writer) throws IOException { protected void readFromStream(ElephantMemoryReader reader) throws IOException, InstantiationException, IllegalAccessException { - reader.skip(length - 8); + data = new byte[(int) (length - 8)]; + reader.read(data); } } diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobHandler.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobHandler.java index 2390ac3b92..8758265650 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobHandler.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobHandler.java @@ -19,6 +19,7 @@ public abstract class BlobHandler { knownBlobs.put(Long.valueOf(EmbeddedSignature.CSMAGIC_EMBEDDED_SIGNATURE), EmbeddedSignature.class); knownBlobs.put(Long.valueOf(Requirements.CSMAGIC_REQUIREMENTS), Requirements.class); knownBlobs.put(Long.valueOf(Entitlements.CSMAGIC_EMBEDDED_ENTITLEMENTS), Entitlements.class); + knownBlobs.put(Long.valueOf(EntitlementsDer.CSMAGIC_EMBEDDED_ENTITLEMENTS_DER), EntitlementsDer.class); knownBlobs.put(Long.valueOf(BlobWrapper.CSMAGIC_BLOB_WRAPPER), BlobWrapper.class); knownBlobs.put(Long.valueOf(CodeDirectory.CSMAGIC_CODEDIRECTORY), CodeDirectory.class); } diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobWrapper.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobWrapper.java index 78f821e69d..f26813fc12 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobWrapper.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/BlobWrapper.java @@ -25,7 +25,7 @@ import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; -import org.bouncycastle.x509.X509Store; +import org.bouncycastle.util.Store; public class BlobWrapper extends BlobCore { /** @@ -34,20 +34,20 @@ public class BlobWrapper extends BlobCore { */ public static final long CSMAGIC_BLOB_WRAPPER = 0xfade0b01; - private CodeDirectory codeDirectory; - private CMSSignedDataGenerator signedDataGenerator; public BlobWrapper() { super(CSMAGIC_BLOB_WRAPPER); } - public BlobWrapper(KeyStore keyStore, X509Store certStore, CodeDirectory codeDirectory) + public BlobWrapper(KeyStore keyStore, Store certStore, CodeDirectory codeDirectory) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, CertificateEncodingException, OperatorCreationException, IOException, CMSException { super(CSMAGIC_BLOB_WRAPPER); - this.codeDirectory = codeDirectory; + sign(keyStore, certStore, codeDirectory); + } + public void sign(KeyStore keyStore, Store certStore, CodeDirectory codeDirectory) throws IOException, CMSException, KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, CertificateEncodingException, OperatorCreationException { signedDataGenerator = new CMSSignedDataGenerator(); String firstAlias = (String) keyStore.aliases().nextElement(); PrivateKey priv = (PrivateKey) (keyStore.getKey(firstAlias, "".toCharArray())); @@ -58,12 +58,8 @@ public BlobWrapper(KeyStore keyStore, X509Store certStore, CodeDirectory codeDir SignerInfoGenerator signerGenerator = new JcaSignerInfoGeneratorBuilder(digestCalculator).build(sha1Signer, cert); signedDataGenerator.addSignerInfoGenerator(signerGenerator); - signedDataGenerator.addCertificates(certStore); - - sign(); - } - - public void sign() throws IOException, CMSException { + signedDataGenerator.addCertificates(certStore); + byte[] rawData = codeDirectory.getBytes(); CMSProcessableByteArray content = (rawData != null) ? new CMSProcessableByteArray(rawData) : null; CMSSignedData sign = signedDataGenerator.generate(content, false); diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/CodeDirectory.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/CodeDirectory.java index 1c928eca8a..f3870e4e0b 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/CodeDirectory.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/CodeDirectory.java @@ -10,6 +10,7 @@ import org.bouncycastle.crypto.digests.GeneralDigest; import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; import tc.tools.deployer.ipa.ElephantMemoryReader; import tc.tools.deployer.ipa.ElephantMemoryWriter; @@ -21,11 +22,16 @@ public class CodeDirectory extends BlobCore { /** http://opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-55032/lib/cscdefs.h */ public static final long CSMAGIC_CODEDIRECTORY = 0xfade0c02; + private static final int VERSION_1 = 0x20100; + private static final int VERSION_2 = 0x20200; + private static final int VERSION_3 = 0x20300; + private static final int VERSION_4 = 0x20400; + /** - * Types of hashes supported. Actually, right now, only SHA1 is really supported. + * Types of hashes supported. */ public static final int cdHashTypeSHA1 = 1; - public static final int cdHashTypeSHA256 = 1; + public static final int cdHashTypeSHA256 = 2; /** * Special hash slot values. In a CodeDirectory, these show up at negative slot indices. This enumeration is also @@ -50,18 +56,65 @@ public class CodeDirectory extends BlobCore { public byte spare1; public byte pageSize; public long spare2; - public long scatterOffset; + + /* Version 0x20100 */ + public long scatterOffset = 0; /* offset of optional scatter vector */ + + /* Version 0x20200 */ + public long teamIdOffset = 0; /* offset of optional team identifier */ + + /* Version 0x20300 */ + public long spare3; /* unused (must be zero) */ + public long codeLimit64; /* limit to main image signature range, 64 bits */ + + /* Version 0x20400 */ + public long execSegBase; /* offset of executable segment */ + public long execSegLimit; /* limit of executable segment */ + public long execSegFlags; /* executable segment flags */ public String identifier; + public String teamId; public int actualPageSize; public byte[] hashes; - protected GeneralDigest hashDigest = new SHA1Digest(); + protected GeneralDigest hashDigest/* = new SHA1Digest() */; + + enum HashType { + NOHASH(null), + SHA1(new SHA1Digest()), + SHA256(new SHA256Digest()); + // HASHTYPE_SHA256 hashType = 2 + // HASHTYPE_SHA256_TRUNCATED hashType = 3 + // HASHTYPE_SHA384 hashType = 4 + // HASHTYPE_SHA512 hashType = 5 + + public final GeneralDigest digest; + + HashType(GeneralDigest digest) { + this.digest = digest; + } + + public void update(byte[] input, int inputOffset, int length, byte[] output, int outputOffset) { + if(digest != null) { + digest.reset(); + digest.update(input, inputOffset, length); + digest.doFinal(output, outputOffset); + } + } + } public CodeDirectory() { super(CSMAGIC_CODEDIRECTORY); } + /** + * We no longer create code directories from scratch, now we just update + * the original one. + * + * @param identifier + * @param codeLimit + */ + @Deprecated public CodeDirectory(String identifier, long codeLimit) { super(CSMAGIC_CODEDIRECTORY); this.scatterOffset = 0; @@ -75,7 +128,7 @@ public CodeDirectory(String identifier, long codeLimit) { this.nCodeSlots = (long) ((this.codeLimit + actualPageSize - 1) / actualPageSize); this.nSpecialSlots = cdSlotMax; this.flags = 0; - this.version = 0x20100; + this.version = VERSION_1; this.identifier = identifier; this.hashes = new byte[(int) ((this.nSpecialSlots + this.nCodeSlots) * this.hashSize)]; @@ -95,9 +148,7 @@ public void setSpecialSlotHash(int slotIndex, byte[] data) { if (data == null) { Arrays.fill(this.hashes, startIndex, startIndex + this.hashSize, (byte) 0); } else { - hashDigest.reset(); - hashDigest.update(data, 0, data.length); - hashDigest.doFinal(this.hashes, startIndex); + HashType.values()[hashType].update(data, 0, data.length, this.hashes, startIndex); } } @@ -105,16 +156,23 @@ public void setCodeSlotsHashes(byte[] data) { for (int i = 0; i < this.nCodeSlots; i++) { int offset = i * actualPageSize; int pageSize = Math.min((int) (this.codeLimit - offset), actualPageSize); - hashDigest.reset(); - hashDigest.update(data, offset, pageSize); - hashDigest.doFinal(this.hashes, (int) ((this.nSpecialSlots + i) * this.hashSize)); + HashType.values()[hashType].update(data, offset, pageSize, this.hashes, (int) ((this.nSpecialSlots + i) * this.hashSize)); } } @Override protected void writeToStream(ElephantMemoryWriter writer) throws IOException { - // identOffset starts after the next 40 bytes; - this.identOffset = (writer.pos - offset) + 40; + // identOffset starts after the header + this.identOffset = (writer.pos - offset) + Integer.SIZE * 8 + Byte.SIZE + 4; + + // The implicit fallthrough is intended + switch ((int) version) { + case VERSION_4: this.identOffset += 3 * Long.SIZE; + case VERSION_3: this.identOffset += Long.SIZE + Integer.SIZE; + case VERSION_2: this.identOffset += Integer.SIZE; + case VERSION_1: this.identOffset += Integer.SIZE; + break; + } // hashOffset starts after the identifier and the hashes of the special slots this.hashOffset = this.identOffset + (identifier.length() + 1) + (this.hashSize * this.nSpecialSlots); writer.writeUnsignedInt(this.version); @@ -129,10 +187,31 @@ protected void writeToStream(ElephantMemoryWriter writer) throws IOException { writer.write(this.spare1); writer.write(this.pageSize); writer.writeUnsignedInt(this.spare2); - writer.writeUnsignedInt(this.scatterOffset); + + if (this.version >= VERSION_1) { + writer.writeUnsignedInt(0); // scatter is being ignored + } + if (this.version >= VERSION_2) { + this.teamIdOffset = this.hashOffset + (this.hashSize * this.nCodeSlots); + writer.writeUnsignedInt(this.teamIdOffset); + } + if (this.version >= VERSION_3) { + writer.writeUnsignedInt(this.spare3); + writer.writeUnsignedLong(this.codeLimit64); + } + if (this.version >= VERSION_4) { + writer.writeUnsignedLong(this.execSegBase); + writer.writeUnsignedLong(this.execSegLimit); + writer.writeUnsignedLong(this.execSegFlags); + } + writer.write(this.identifier.getBytes("US-ASCII")); writer.write((byte) 0); // write string delimiter writer.write(this.hashes); + if (this.teamIdOffset > 0) { + writer.write(this.teamId.getBytes("US-ASCII")); + writer.write((byte) 0); // write string delimiter + } } @Override @@ -150,11 +229,29 @@ protected void readFromStream(ElephantMemoryReader reader) throws IOException { this.pageSize = (byte) reader.read(); this.actualPageSize = 1 << this.pageSize; this.spare2 = reader.readUnsignedInt(); - this.scatterOffset = reader.readUnsignedInt(); + if (this.version >= VERSION_1) { + this.scatterOffset = reader.readUnsignedInt(); + } + if (this.version >= VERSION_2) { + this.teamIdOffset = reader.readUnsignedInt(); + } + if (this.version >= VERSION_3) { + this.spare3 = reader.readUnsignedInt(); + this.codeLimit64 = reader.readUnsignedLong(); + } + if (this.version >= VERSION_4) { + this.execSegBase = reader.readUnsignedLong(); + this.execSegLimit = reader.readUnsignedLong(); + this.execSegFlags = reader.readUnsignedLong(); + } reader.memorize(); reader.moveTo(offset + identOffset); this.identifier = reader.readString(); + if (this.teamIdOffset > 0) { + reader.moveTo(offset + teamIdOffset); + this.teamId = reader.readString(); + } reader.moveBack(); long num4 = this.nSpecialSlots + this.nCodeSlots; this.hashes = new byte[(int) (num4 * this.hashSize)]; diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EmbeddedSignature.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EmbeddedSignature.java index 7cb17dfb05..e86fecde82 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EmbeddedSignature.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EmbeddedSignature.java @@ -6,8 +6,18 @@ package tc.tools.deployer.ipa.blob; import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateEncodingException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.bouncycastle.cms.CMSException; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.util.Store; public class EmbeddedSignature extends SuperBlob { /** http://opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-55032/lib/cscdefs.h */ @@ -16,12 +26,16 @@ public class EmbeddedSignature extends SuperBlob { public static final long CSSLOT_CODEDIRECTORY = 0L; public static final long CSSLOT_REQUIREMENTS = 2L; public static final long CSSLOT_ENTITLEMENTS = 5L; + public static final long CSSLOT_ENTITLEMENTS_DER = 7L; + public static final long CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000; public static final long CSSLOT_BLOBWRAPPER = 0x10000; public CodeDirectory codeDirectory; - Entitlements entitlements; + public Entitlements entitlements; + EntitlementsDer entitlementsDer; Requirements requirements; BlobWrapper blobWrapper; + public List alternateCodeDirectories = new ArrayList(); public EmbeddedSignature() { super(CSMAGIC_EMBEDDED_SIGNATURE); @@ -48,6 +62,12 @@ public void add(BlobIndex blobIndex) { case (int) CSSLOT_ENTITLEMENTS: entitlements = (Entitlements) blobIndex.blob; break; + case (int) CSSLOT_ENTITLEMENTS_DER: + entitlementsDer = (EntitlementsDer) blobIndex.blob; + break; + case (int) CSSLOT_ALTERNATE_CODEDIRECTORIES: + alternateCodeDirectories.add((CodeDirectory) blobIndex.blob); + break; case (int) CSSLOT_BLOBWRAPPER: blobWrapper = (BlobWrapper) blobIndex.blob; break; @@ -57,7 +77,27 @@ public void add(BlobIndex blobIndex) { super.add(blobIndex); } - public void sign() throws IOException, CMSException { - blobWrapper.sign(); + public void sign(KeyStore keyStore, Store certStore) throws IOException, CMSException, UnrecoverableKeyException, CertificateEncodingException, KeyStoreException, NoSuchAlgorithmException, OperatorCreationException { + blobWrapper.sign(keyStore, certStore, codeDirectory); + } + + public void setBundleIdentifier(String bundleIdentifier) { + codeDirectory.identifier = bundleIdentifier; + for (CodeDirectory cd : alternateCodeDirectories) { + cd.identifier = bundleIdentifier; + } + } + + public void updateCodeDirectoryHashes(byte[] data, byte[] info, byte[] sourceData, Object object) throws IOException { + byte[] requirementsBytes = requirements.getBytes(); + byte[] entitlementsBytes = entitlements.getBytes(); + + List cds = new ArrayList<>(alternateCodeDirectories.size() + 1); + Collections.copy(alternateCodeDirectories, cds); + cds.add(codeDirectory); + cds.forEach(cd -> { + cd.setSpecialSlotsHashes(info, requirementsBytes, sourceData, null, entitlementsBytes); + cd.setCodeSlotsHashes(data); + }); } } diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/Entitlements.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/Entitlements.java index 45f46058b9..9bf756ffef 100644 --- a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/Entitlements.java +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/Entitlements.java @@ -17,4 +17,8 @@ public Entitlements(byte[] data) { this(); this.data = data; } + + public void setData(byte[] data) { + this.data = data; + } } diff --git a/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EntitlementsDer.java b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EntitlementsDer.java new file mode 100644 index 0000000000..0eb283c5f0 --- /dev/null +++ b/TotalCrossSDK/src/main/java/tc/tools/deployer/ipa/blob/EntitlementsDer.java @@ -0,0 +1,15 @@ +package tc.tools.deployer.ipa.blob; + +public class EntitlementsDer extends BlobCore { + /** https://bitbucket.org/khooyp/gdb/src/c3a263c415ad/include/mach-o/codesign.h */ + public static final long CSMAGIC_EMBEDDED_ENTITLEMENTS_DER = 0xfade7172; + + public EntitlementsDer() { + super(CSMAGIC_EMBEDDED_ENTITLEMENTS_DER); + } + + public EntitlementsDer(byte[] data) { + this(); + this.data = data; + } +} diff --git a/TotalCrossSDK/src/main/java/totalcross/Launcher.java b/TotalCrossSDK/src/main/java/totalcross/Launcher.java index df9fe89be8..fbcef23bdf 100644 --- a/TotalCrossSDK/src/main/java/totalcross/Launcher.java +++ b/TotalCrossSDK/src/main/java/totalcross/Launcher.java @@ -27,7 +27,7 @@ import java.awt.event.MouseWheelListener; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; -import java.awt.image.MemoryImageSource; +import java.awt.image.DataBufferInt; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -46,6 +46,11 @@ import net.coobird.thumbnailator.Thumbnails; import tc.tools.AnonymousUserData; +import net.coobird.thumbnailator.Thumbnails.Builder; +import net.coobird.thumbnailator.resizers.configurations.Antialiasing; +import net.coobird.thumbnailator.resizers.configurations.Dithering; +import net.coobird.thumbnailator.resizers.configurations.Rendering; +import net.coobird.thumbnailator.resizers.configurations.ScalingMode; import tc.tools.JarClassPathLoader; import tc.tools.deployer.DeploySettings; import totalcross.io.IOException; @@ -112,8 +117,8 @@ final public class Launcher extends java.applet.Applet implements WindowListener private int lookupR[], lookupG[], lookupB[], lookupGray[]; private int pal685[]; private Class _class; // used by the openInputStream method. - protected MemoryImageSource screenMis; - protected java.awt.Image screenImg; + protected BufferedImage screenImg; + private Builder thumbnailBuilder; private AlertBox alert; private String frameTitle; private String crid4settings; // prevent from having two different crids for loading and storing the settings. @@ -125,6 +130,8 @@ final public class Launcher extends java.applet.Applet implements WindowListener private double toScaleValue = -1; private double toDensityValue = 1; + public totalcross.ui.Insets toInsetsPortrait; + public totalcross.ui.Insets toInsetsLandscape; @SuppressWarnings("deprecation") public Launcher() { @@ -134,10 +141,12 @@ public Launcher() { addMouseWheelListener(this); addMouseMotionListener(this); try { - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/jna-4.2.2.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/jna-platform-4.2.2.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/slf4j-api-1.7.21.jar"); - JarClassPathLoader.addFile(DeploySettings.etcDir + "libs/appdirs-1.0.0.jar"); + File libsFile = new File(DeploySettings.distDir, "libs"); + JarClassPathLoader.addJar(libsFile, "jna"); + JarClassPathLoader.addJar(libsFile, "jna-platform"); + JarClassPathLoader.addJar(libsFile, "slf4j-api"); + JarClassPathLoader.addJar(libsFile, "appdirs"); + JarClassPathLoader.addJar(libsFile, "thumbnailator"); } catch (java.io.IOException e) { e.printStackTrace(); } @@ -407,10 +416,11 @@ static void showInstructions() { System.out.println(" /scr WIDTHxHEIGHT : sets the width and height resolution."); System.out.println(" /scr WIDTHxHEIGHTxBPP : sets the width, height and bits per pixel (8, 16, 24 or 32)"); System.out.println(" /density <0.1 to 4> : sets the screen pixel density"); - System.out.println(" /scr win32 : Windows 32 (same of /scr 240x320x24)"); - System.out.println("* /scr android : Android (same of /scr 320x568x24)"); - System.out.println(" /scr iphone : iPhone 8 resolution (same of /scr 750x1334x24 /density 2)"); - System.out.println(" /scr ipad : iPad resolution (same of /scr 1536x2048x24 /density 2)"); + System.out.println(" /scr win32 : Windows 32 (same of /scr 240x320x24)"); + System.out.println("* /scr android : Android (same of /scr 360x592x24 /density 2)"); + System.out.println(" /scr iPhone : iPhone 15 resolution (same of /scr 393x852x24 /density 3 /safeAreaPortrait 59,0,34,0 /safeAreaLandscape 0,59,21,59)"); + System.out.println(" /scr iPhoneSE : iPhone SE resolution (same of /scr 375x667x24 /density 2)"); + System.out.println(" /scr ipad : iPad resolution (same of /scr 768x1024x24 /density 2)"); System.out.println(" /fullscreen : Use full-screen window"); System.out.println(" /pos x,y : Sets the openning position of the application"); System.out.println(" /uiStyle Flat : Flat user interface style"); @@ -433,6 +443,8 @@ static void showInstructions() { System.out.println(" /dataPath : sets where the PDB and media files are stored"); System.out.println(" /cmdLine <...> : the rest of arguments-1 are passed as the command line"); System.out.println(" /fontSize : set the default font size to the one passed as parameter"); + System.out.println(" /safeAreaPortrait top,left,bottom,right : sets a margin from the device borders to simulate devices with notch on portrait"); + System.out.println(" /safeAreaLandscape top,left,bottom,right : sets a margin from the device borders to simulate devices with notch on landscape"); System.out.println("The class name that extends MainWindow must always be the last argument"); System.out.println("Please notice that the Launcher automatically scales down the resolution to fit in the display, to disable this behavior you may include the argument scale with the value 1"); } @@ -499,18 +511,25 @@ protected void parseArguments(String clazz, String... args) { toHeight = 320; toBpp = 24; } else if (next.equalsIgnoreCase("iPhone")) { - toWidth = 750; - toHeight = 1334; + toWidth = 393; + toHeight = 852; + toBpp = 24; + toDensityValue = 3; + toInsetsPortrait = new totalcross.ui.Insets(59, 0, 34, 0); + toInsetsLandscape = new totalcross.ui.Insets(0, 59, 21, 59); + } else if (next.equalsIgnoreCase("iPhoneSE")) { + toWidth = 375; + toHeight = 667; toBpp = 24; toDensityValue = 2; } else if (next.equalsIgnoreCase("ipad")) { - toWidth = 1536; - toHeight = 2048; - toBpp = 24; - toDensityValue = 2; + toWidth = 768; + toHeight = 1024; + toBpp = 24; + toDensityValue = 2; } else if (next.equalsIgnoreCase("android")) { - toWidth = 720; - toHeight = 1184; + toWidth = 360; + toHeight = 592; toBpp = 24; toDensityValue = 2; } else { @@ -527,6 +546,18 @@ protected void parseArguments(String clazz, String... args) { System.out.println("Screen is " + toWidth + "x" + toHeight + "x" + toBpp); } else if (args[i].equalsIgnoreCase("/fullscreen")) { fullscreen = true; + } else if (args[i].equalsIgnoreCase("/safeAreaPortrait")) { + String[] scr = tokenizeString(args[++i].toLowerCase(), ','); + if (scr.length != 4) { + throw new Exception("Argument /safeAreaPortrait expects 4 comma separated values in the following format: top,left,bottom,right"); + } + toInsetsPortrait = new totalcross.ui.Insets(toInt(scr[0]), toInt(scr[1]), toInt(scr[2]), toInt(scr[3])); + } else if (args[i].equalsIgnoreCase("/safeAreaLandscape")) { + String[] scr = tokenizeString(args[++i].toLowerCase(), ','); + if (scr.length != 4) { + throw new Exception("Argument /safeAreaLandscape expects 4 comma separated values in the following format: top,left,bottom,right"); + } + toInsetsLandscape = new totalcross.ui.Insets(toInt(scr[0]), toInt(scr[1]), toInt(scr[2]), toInt(scr[3])); } else if (args[i].equalsIgnoreCase("/r")) { ++i; } else if (args[i].equalsIgnoreCase("/pos")) /* x,y */ @@ -638,6 +669,20 @@ protected void parseArguments(String clazz, String... args) { } Settings.screenDensity = toDensityValue; + toWidth *= toDensityValue; + toHeight *= toDensityValue; + if (toInsetsPortrait != null) { + toInsetsPortrait.top *= toDensityValue; + toInsetsPortrait.left *= toDensityValue; + toInsetsPortrait.bottom *= toDensityValue; + toInsetsPortrait.right *= toDensityValue; + } + if (toInsetsLandscape != null) { + toInsetsLandscape.top *= toDensityValue; + toInsetsLandscape.left *= toDensityValue; + toInsetsLandscape.bottom *= toDensityValue; + toInsetsLandscape.right *= toDensityValue; + } /* * Gets the display resolution and automatically scales down the Launcher to fit @@ -873,13 +918,13 @@ private void takeScreenShot() { } private void screenResized(int w, int h, boolean setframe) { - if (screenMis == null || (Settings.screenWidth == w && Settings.screenHeight == h)) { + if (screenImg == null || (Settings.screenWidth == w && Settings.screenHeight == h)) { return; } Settings.screenWidth = w; Settings.screenHeight = h; frame.setFrameSize(w, h, setframe); - screenMis = null; // force the creation of a new screen image + screenImg = null; // force the creation of a new screen image eventThread.pushEvent(KeyEvent.SPECIAL_KEY_PRESS, SpecialKeys.SCREEN_CHANGE, 0, 0, modifiers, Vm.getTimeStamp()); } @@ -1176,6 +1221,31 @@ private int getScreenColor(int p) { public void updateScreen() { //int ini = totalcross.sys.Vm.getTimeStamp(); + int w = totalcross.sys.Settings.screenWidth; + int h = totalcross.sys.Settings.screenHeight; + int ww = (int) (w * toScale); + int hh = (int) (h * toScale); + + if (screenImg == null) { + screenImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + // We can typecast directly to DataBufferInt because that's the type used for images with 24+ bit color + DataBufferInt dbi = (DataBufferInt) screenImg.getRaster().getDataBuffer(); + int[] pixels = dbi.getData(); + // Copy whatever was drawn before + System.arraycopy(totalcross.ui.gfx.Graphics.mainWindowPixels, 0, pixels, 0, w*h); + // And replace with our bytes, now we no longer need to copy from the mainWindowPixels to our image. Saving a ton of memory. + totalcross.ui.gfx.Graphics.mainWindowPixels = pixels; + if (toScale != 1 && !fastScale) { + // And as a bonus, we can reuse the thumbnailator builder because it always references the same object. + thumbnailBuilder = Thumbnails + .of(screenImg) + .size(ww, hh) + .rendering(Rendering.SPEED) + .scalingMode(ScalingMode.PROGRESSIVE_BILINEAR) + .antialiasing(Antialiasing.OFF) + .dithering(Dithering.DISABLE); + } + } int[] pixels = totalcross.ui.gfx.Graphics.mainWindowPixels; int n = Settings.screenWidth * Settings.screenHeight; if (toBpp >= 24) { @@ -1210,29 +1280,14 @@ public void updateScreen() { break; } } - int w = totalcross.sys.Settings.screenWidth; - int h = totalcross.sys.Settings.screenHeight; - if (screenMis == null) { - screenMis = new MemoryImageSource(w, h, - GraphicsEnvironment. - getLocalGraphicsEnvironment().getDefaultScreenDevice(). - getDefaultConfiguration().getColorModel(), - screenPixels, 0, w); - screenMis.setAnimated(true); - screenMis.setFullBufferUpdates(true); - screenImg = Toolkit.getDefaultToolkit().createImage(screenMis); - } - screenMis.newPixels(); Graphics g = getGraphics(); - int ww = (int) (w * toScale); - int hh = (int) (h * toScale); int shiftY = totalcross.ui.Window.shiftY; int shiftH = totalcross.ui.Window.shiftH; if ((shiftY + shiftH) > h) { totalcross.ui.Window.shiftY = shiftY = h - shiftH; } if (shiftY != 0) { - g.setColor(new Color(UIColors.shiftScreenColor)); + g.setColor(new Color(UIColors.unsafeAreaColor)); int yy = (int) (shiftH * toScale); g.fillRect(0, yy, ww, hh - yy); // erase empty area g.setClip(0, 0, ww, yy); // limit drawing area @@ -1244,7 +1299,7 @@ public void updateScreen() { g.drawImage(screenImg, 0, 0, ww, hh, 0, 0, w, h, this); } else { try { - g.drawImage(Thumbnails.of(toBufferedImage(screenImg)).size(ww, hh).asBufferedImage(), 0, 0, this); + g.drawImage(thumbnailBuilder.asBufferedImage(), 0, 0, this); } catch (java.io.IOException e) { e.printStackTrace(); } @@ -1257,7 +1312,7 @@ public void updateScreen() { g.setClip(0, 0, ww, hh); } // make the emulator work like OpenGL: erase the screen to instruct the user that everything must be drawn always - //java.util.Arrays.fill(pixels, getScreenColor(UIColors.shiftScreenColor)); + //java.util.Arrays.fill(pixels, getScreenColor(UIColors.unsafeAreaColor)); } public static BufferedImage toBufferedImage(java.awt.Image img) { @@ -2612,9 +2667,6 @@ public void run() { public void setSIP(int option, Control edit, boolean secret) { } - public static void checkLitebaseAllowed() { - } - @Override public void componentHidden(ComponentEvent arg0) { } diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/DB.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/DB.java index cb24922409..e2a78e5b9d 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/DB.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/DB.java @@ -18,6 +18,8 @@ import java.sql.BatchUpdateException; import java.sql.SQLException; +import totalcross.util.Vector; + /* * This class is the interface to SQLite. It provides some helper functions * used by other parts of the driver. The goal of the helper functions here @@ -39,8 +41,8 @@ abstract class DB implements Codes { long commit; /** Tracer for statements to avoid unfinalized statements on db close. */ - //private Vector stmts = new Vector(10);//private final Map stmts = new HashMap(); - //public totalcross.util.concurrent.Lock stmtsLock = new totalcross.util.concurrent.Lock(); + private Vector stmts = new Vector(10);//private final Map stmts = new HashMap(); + public totalcross.util.concurrent.Lock stmtsLock = new totalcross.util.concurrent.Lock(); public static interface ProgressObserver { public void progress(int remaining, int pageCount); @@ -117,6 +119,8 @@ public static interface ProgressObserver { * @see http://www.sqlite.org/c3ref/load_extension.html */ abstract int enable_load_extension(boolean enable) throws SQLException; + + abstract int enable_see_extension() throws SQLException; /** * Executes an SQL statement using the process of compiling, evaluating, and destroying the @@ -172,11 +176,11 @@ void close() throws SQLException { } closed = true; // finalize any remaining statements before closing db - // synchronized (stmtsLock) - // { - // for (int i = stmts.size(); --i >= 0;) - // ((Stmt)stmts.items[i]).close(); - // } + synchronized (stmtsLock) + { + for (int i = stmts.size(); --i >= 0;) + ((Stmt)stmts.items[i]).close(); + } // remove memory used by user-defined functions //free_functions(); @@ -205,10 +209,10 @@ void prepare(Stmt stmt) throws SQLException { finalize(stmt); } stmt.pointer = prepare(stmt.sql); - // synchronized (stmtsLock) - // { - // stmts.addElement(stmt); - // } + synchronized (stmtsLock) + { + stmts.addElement(stmt); + } } @Override diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/MetaData.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/MetaData.java index 48d61db80c..a17fae7fe6 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/MetaData.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/MetaData.java @@ -157,19 +157,21 @@ public Connection getConnection() { } /** + * @throws SQLException * @see java.sql.DatabaseMetaData#getDatabaseMajorVersion() */ @Override - public int getDatabaseMajorVersion() { - return 3; + public int getDatabaseMajorVersion() throws SQLException { + return Integer.parseInt(this.conn.libversion().split("\\.")[0]); } /** + * @throws SQLException * @see java.sql.DatabaseMetaData#getDatabaseMinorVersion() */ @Override - public int getDatabaseMinorVersion() { - return 0; + public int getDatabaseMinorVersion() throws SQLException { + return Integer.parseInt(this.conn.libversion().split("\\.")[1]); } /** @@ -401,7 +403,7 @@ public String getDatabaseProductName() { */ @Override public String getDatabaseProductVersion() throws SQLException { - return conn.libversion(); + return this.conn.libversion(); } /** diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/NativeDB.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/NativeDB.java index 632337ee98..090d5d2645 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/NativeDB.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/NativeDB.java @@ -65,6 +65,9 @@ final class NativeDB extends DB { @Override native int enable_load_extension(boolean enable); + @Override + native int enable_see_extension(); + /** * @see org.sqlite.DB#interrupt() */ diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConfig.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConfig.java index 4a1fe88a51..0d5967cd36 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConfig.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConfig.java @@ -133,6 +133,12 @@ public void apply(Connection conn) throws SQLException { pragmaParams.remove(Pragma.DATE_PRECISION.pragmaName); pragmaParams.remove(Pragma.DATE_CLASS.pragmaName); pragmaParams.remove(Pragma.DATE_STRING_FORMAT.pragmaName); + pragmaParams.put("key", ""); + pragmaParams.put("hexkey", ""); + pragmaParams.put("textkey", ""); + pragmaParams.put("rekey", ""); + pragmaParams.put("hexrekey", ""); + pragmaParams.put("textrekey", ""); Statement stat = conn.createStatement(); try { diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConnection.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConnection.java index 4c9ea7dd74..a5327ead3a 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConnection.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteConnection.java @@ -111,6 +111,7 @@ public SQLiteConnection(String url, String fileName, Hashtable prop) throws SQLE db.shared_cache(config.isEnabledSharedCache()); } db.enable_load_extension(config.isEnabledLoadExtension()); + db.enable_see_extension(); // set pragmas config.apply(this); @@ -360,6 +361,7 @@ public void setReadOnly(boolean ro) throws SQLException { * @throws SQLException * @see java.sql.Connection#getMetaData() */ + @Override public DatabaseMetaData getMetaData() throws SQLException { checkOpen(); diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteUtil.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteUtil.java index 9f1d75d586..f93892a9f9 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteUtil.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/SQLiteUtil.java @@ -19,7 +19,7 @@ import totalcross.util.InvalidDateException; import totalcross.util.Vector; -/** Utility class to make convertion from Litebase to SQLite easier. +/** Utility class to make convertion to SQLite easier. * * Important rules about date and time in SQLite. * @@ -290,8 +290,7 @@ public static Date fromSqlDate(String sqldate) throws InvalidDateException { return new Date(sp == -1 ? sqldate : sqldate.substring(0, sp), Settings.DATE_YMD); } - /** Rebuild and shrink the entire database, like the old Litebase's purge method, - * but in this case it applies to all tables. + /** Rebuild and shrink the entire database. */ public void shrinkDB() throws SQLException { Statement st = con().createStatement(); diff --git a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/ui/DBListBox.java b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/ui/DBListBox.java index 3060e7896a..21dbb7a918 100644 --- a/TotalCrossSDK/src/main/java/totalcross/db/sqlite/ui/DBListBox.java +++ b/TotalCrossSDK/src/main/java/totalcross/db/sqlite/ui/DBListBox.java @@ -17,7 +17,7 @@ * only. The background color of the control will be a lighter version of the * given color. *

    - * Note: this is a special version for the LitebaseConnection. It accepts + * Note: this is a special version. It accepts * a String matrix as the input, and does not allow add/remove/set of elements, * since it reflects the database and must be filled again after some row is * updated. @@ -25,8 +25,6 @@ * Here is an example showing how it can be used: * *

    - * import litebase.ui.*;
    - * import litebase.*;
      *
      * public class MyProgram extends totalcross.ui.MainWindow
      * {
    diff --git a/TotalCrossSDK/src/main/java/totalcross/io/ByteArrayStream.java b/TotalCrossSDK/src/main/java/totalcross/io/ByteArrayStream.java
    index 66e8c11b52..7117493d4d 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/io/ByteArrayStream.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/io/ByteArrayStream.java
    @@ -193,7 +193,7 @@ public int reuse() // guich@401_34
       public int writeBytes(byte buf[], int start, int count) {
         if (len < (count + pos)) // grow the buffer
         {
    -      int size = (count + pos) * 12 / 10; // grows 20% above the new needed capacity
    +      int size = (int) ((count + pos) * 1.2); // grows 20% above the new needed capacity
           byte[] newBuffer = new byte[size];
           Vm.arrayCopy(buffer, 0, newBuffer, 0, pos);
           buffer = newBuffer;
    diff --git a/TotalCrossSDK/src/main/java/totalcross/io/File.java b/TotalCrossSDK/src/main/java/totalcross/io/File.java
    index d7124db314..d4b81fce44 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/io/File.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/io/File.java
    @@ -1335,4 +1335,28 @@ public byte[] read() throws IOException {
         readBytes(ret, 0, len);
         return ret;
       }
    +
    +  public final static String DIRECTORY_PICTURES = "Pictures";
    +  public final static String DIRECTORY_DOCUMENTS = "Documents";
    +  public final static String DIRECTORY_DOWNLOADS = "Downloads";
    +
    +  /**
    +   * Returns a file object with a path writable by the application on the device
    +   * storage.
    +   * 
    +   * @param type
    +   * @param dirName
    +   * @return
    +   * @throws IOException
    +   */
    +  public static File getAppSpecificDir(String type, String dirName) throws IOException {
    +    if (type == null) {
    +      throw new NullPointerException("Argument type is null");
    +    }
    +    java.io.File file = new java.io.File(type, dirName);
    +    if (file == null || !file.mkdirs()) {
    +      throw new IOException();
    +    }
    +    return new File(file.getAbsolutePath(), DONT_OPEN);
    +  }
     }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/io/File4D.java b/TotalCrossSDK/src/main/java/totalcross/io/File4D.java
    index ff4f541913..b78626e991 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/io/File4D.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/io/File4D.java
    @@ -366,4 +366,10 @@ public void writeAndClose(byte[] bytes) throws IOException {
           close();
         }
       }
    +
    +  public final static String DIRECTORY_PICTURES = "Pictures";
    +  public final static String DIRECTORY_DOCUMENTS = "Documents";
    +  public final static String DIRECTORY_DOWNLOADS = "Downloads";
    +
    +  public static native File getAppSpecificDir(String type, String dirName) throws IOException;
     }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/io/device/gps/GPS.java b/TotalCrossSDK/src/main/java/totalcross/io/device/gps/GPS.java
    index b8ed2da670..fb065799b3 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/io/device/gps/GPS.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/io/device/gps/GPS.java
    @@ -522,6 +522,21 @@ private String validateMessage(String message) {
         return new String(msgChars, 0, msgChars.length - 2);
       }
     
    +  public final static class EphemeridesData {
    +
    +    private EphemeridesData() {
    +    }
    +
    +    @ReplacedByNativeOnDeploy
    +    public static void delete() {
    +    }
    +
    +    @ReplacedByNativeOnDeploy
    +    public static void download() {
    +    }
    +
    +  }
    +
       @Override
       protected void finalize() {
         this.stop();
    diff --git a/TotalCrossSDK/src/main/java/totalcross/json/JSONObject.java b/TotalCrossSDK/src/main/java/totalcross/json/JSONObject.java
    index 3c750f75eb..7391cce396 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/json/JSONObject.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/json/JSONObject.java
    @@ -1,5 +1,6 @@
     package totalcross.json;
     
    +import java.io.Reader;
     /*
      Copyright (c) 2002 JSON.org
     
    @@ -320,6 +321,18 @@ public JSONObject(String source) throws JSONException {
         this(new JSONTokener(source));
       }
     
    +  /**
    +   * Construct a JSONObject from a source reader.
    +   * 
    +   * @param source reader to read data from.
    +   * @throws JSONException
    +   *                If there is a syntax error in the source string or a
    +   *                duplicated key.
    +   */
    +  public JSONObject(Reader source) throws JSONException {
    +    this(new JSONTokener(source));
    +  }
    +
       /**
        * Construct a JSONObject from a ResourceBundle.
        *
    diff --git a/TotalCrossSDK/src/main/java/totalcross/json/JSONTokener.java b/TotalCrossSDK/src/main/java/totalcross/json/JSONTokener.java
    index e6629d0cff..b2dbb7d8af 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/json/JSONTokener.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/json/JSONTokener.java
    @@ -1,5 +1,9 @@
     package totalcross.json;
     
    +import java.io.IOException;
    +import java.io.Reader;
    +import java.io.StringReader;
    +
     /*
     Copyright (c) 2002 JSON.org
     
    @@ -38,36 +42,23 @@ public class JSONTokener {
       private long index;
       private long line;
       private char previous;
    -  //private ByteArrayStream  reader;
       private boolean usePrevious;
     
    -  private int idx, len;
    -  private String source;
    +  private Reader source;
    +
    +  private char buffer[] = new char[8 * 1024];
    +  private int bufferPos = Integer.MAX_VALUE;
    +  private int bufferLimit = 0;
     
       /**
        * Construct a JSONTokener from a Reader.
        *
        * @param reader     A reader.
        */
    -  /*    public JSONTokener(ByteArrayStream reader) {
    -        this.reader = reader;//reader.markSupported()? reader: new BufferedReader(reader);
    -        this.eof = false;
    -        this.usePrevious = false;
    -        this.previous = 0;
    -        this.index = 0;
    -        this.character = 1;
    -        this.line = 1;
    -    }
    -   */
     
    -  /**
    -   * Construct a JSONTokener from an InputStream.
    -   * @param inputStream The source.
    -   */
    -  /*    public JSONTokener(InputStream inputStream) throws JSONException {
    -        this(new InputStreamReader(inputStream));
    -    }
    -   */
    +  public JSONTokener(Reader reader) throws JSONException {
    +    source = reader;
    +  }
     
       /**
        * Construct a JSONTokener from a string.
    @@ -75,9 +66,7 @@ public class JSONTokener {
        * @param s     A source string.
        */
       public JSONTokener(String s) {
    -    source = s;
    -    len = s.length();
    -    //this(new ByteArrayStream(s.getBytes()));//StringReader(s));
    +    source = new StringReader(s);
       }
     
       /**
    @@ -143,8 +132,16 @@ public char next() throws JSONException {
           this.usePrevious = false;
           c = this.previous;
         } else {
    -      c = idx == len ? 0 : source.charAt(idx++);//this.reader.read();
    -
    +      if (bufferPos < bufferLimit) {
    +        c = buffer[bufferPos++];
    +      } else {
    +        try {
    +          c = fetch();
    +        } catch (IOException e) {
    +          throw new JSONException(e);
    +        }
    +      }  
    +      
           if (c <= 0) { // End of stream
             this.eof = true;
             c = 0;
    @@ -164,6 +161,32 @@ public char next() throws JSONException {
         return this.previous;
       }
     
    +  private int fetch() throws IOException {
    +
    +    if (bufferPos < bufferLimit) {
    +      return buffer[bufferPos++];
    +    }
    +
    +    if (this.eof == true) {
    +      return -1;
    +    }
    +
    +    bufferPos = 0;
    +
    +    int count = 0;
    +    while (count == 0) {
    +      count = source.read(buffer, bufferPos, buffer.length);
    +      if (count == -1) {
    +        this.eof = true;
    +        bufferLimit = 0;
    +        return -1;
    +      }
    +    }
    +    bufferLimit = count;
    +
    +    return buffer[bufferPos++];
    +  }
    +
       /**
        * Consume the next character, and check that it matches a specified
        * character.
    @@ -233,7 +256,7 @@ public char nextClean() throws JSONException {
        */
       public String nextString(char quote) throws JSONException {
         char c;
    -    StringBuilder sb = new StringBuilder();
    +    StringBuilder sb = new StringBuilder(64);
         for (;;) {
           c = this.next();
           switch (c) {
    @@ -375,17 +398,18 @@ public Object nextValue() throws JSONException {
        * @return The requested character, or zero if the requested character
        * is not found.
        */
    +/* 
       public char skipTo(char to) throws JSONException {
         char c;
         try {
           long startIndex = this.index;
           long startCharacter = this.character;
           long startLine = this.line;
    -      int oldIdx = idx;//this.reader.mark(1000000);
    +      //this.reader.mark(1000000);
           do {
             c = this.next();
             if (c == 0) {
    -          idx = oldIdx;//this.reader.reset();
    +          //this.reader.reset();
               this.index = startIndex;
               this.character = startCharacter;
               this.line = startLine;
    @@ -398,6 +422,7 @@ public char skipTo(char to) throws JSONException {
         this.back();
         return c;
       }
    + */
     
       /**
        * Make a JSONException to signal a syntax error.
    diff --git a/TotalCrossSDK/src/main/java/totalcross/lang/Math4D.java b/TotalCrossSDK/src/main/java/totalcross/lang/Math4D.java
    index b43b0f2109..a04d3b1b7f 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/lang/Math4D.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/lang/Math4D.java
    @@ -1008,4 +1008,22 @@ private static double reducedArcValue(double x) {
         long n = round(INV_PI_2 * x);
         return x - n * PI_2;
       }
    +
    +  public static double hypot(double x, double y) {
    +    x = Math.abs(x);
    +    y = Math.abs(y);
    +
    +    if (x < y) {
    +        double t = x;
    +        x = y;
    +        y = t;
    +    }
    +
    +    if (x == 0.0) {
    +        return 0.0;
    +    }
    +
    +    double r = y / x;
    +    return x * Math.sqrt(1 + r * r);
    +  }
     }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/HttpStream.java b/TotalCrossSDK/src/main/java/totalcross/net/HttpStream.java
    index d4089e8ebd..cee752045e 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/net/HttpStream.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/HttpStream.java
    @@ -6,6 +6,8 @@
     
     package totalcross.net;
     
    +import java.security.NoSuchAlgorithmException;
    +
     import totalcross.io.IOException;
     import totalcross.io.LineReader;
     import totalcross.io.Stream;
    @@ -13,6 +15,7 @@
     import totalcross.net.mail.MessagingException;
     import totalcross.net.mail.Multipart;
     import totalcross.net.mail.Part;
    +import totalcross.net.ssl.SSLContext;
     import totalcross.net.ssl.SSLSocket;
     import totalcross.sys.AbstractCharacterConverter;
     import totalcross.sys.CharacterConverter;
    @@ -23,6 +26,9 @@
     import totalcross.ui.image.ImageException;
     import totalcross.util.Hashtable;
     import totalcross.util.IOUtils;
    +import totalcross.util.zip.CompressedStream;
    +import totalcross.util.zip.GZipStream;
    +import totalcross.util.zip.ZLibStream;
     
     /**
      * A HttpStream HAS-A totalcross.net.Socket and takes care of exchange protocol. It starts reading (in a buffer) at the
    @@ -405,12 +411,16 @@ public static class Options {
         public int writeBytesSize = 1024;
     
         /**
    -     * Socket factory used to create the underlying connection. You may replace it with a SSLSocketFactory to make a
    -     * HTTPS connection over a secure socket, or with your own subclass of SocketFactory.
    +     * Can be used to force HttpStream to use the defined socket factory when
    +     * creating the underlying connection. HttpStream automatically picks the
    +     * default socket factory for HTTP or HTTPS connections
    +     * (SocketFactory.getDefault() and SSLContext.getDefault().getSocketFactory()
    +     * respectively). Use this field only if you wish to replace this default
    +     * behavior.
          * 
          * @since TotalCross 1.6
          */
    -    public SocketFactory socketFactory = SocketFactory.getDefault();
    +    public SocketFactory socketFactory = null;
     
         /**
          * Charset encoding ISO-8859-1
    @@ -429,6 +439,18 @@ public static class Options {
          */
         private String encoding = CHARSET_ISO88591;
     
    +    /**
    +     * The HttpStream request will ask the server for deflate or gzip encoded
    +     * response by default. This behaviour can be changed by either setting this
    +     * field to true or by explicitly adding the header Accept-Encoding to the
    +     * request.
    +     * 
    + * Please notice that HttpStream automatically detects and inflates the response + * received from the stream if a supported content encoding (deflate or gzip) is + * detected. + */ + public boolean disableEncoding = false; + /** Constructs a new Options class, from where you change the behaviour of an Http connection. * Sets the postHeaders to: *
    @@ -611,33 +633,13 @@ public boolean mustSendData() {
     
       private AbstractCharacterConverter cc = (CharacterConverter) Convert.charsetForName("ISO-8859-1");
     
    +  private Stream contentStream;
    +
       @Override
       public int readBytes(byte buf[], int start, int count) throws totalcross.io.IOException {
    -    int lastRead = IOUtils.EOF;
    -    int bytesRead = 0;
    -
    -    if (ofsStart < ofsEnd) {
    -      int len = ofsEnd - ofsStart;
    -      if (count < len) {
    -        Vm.arrayCopy(buffer, ofsStart, buf, start, count);
    -        ofsStart += count;
    -        bytesRead = count;
    -      } else {
    -        Vm.arrayCopy(buffer, ofsStart, buf, start, len);
    -        ofsStart = ofsEnd;
    -        buffer = null;
    -        bytesRead = len;
    -        if (count > len) {
    -          lastRead = socket.readBytes(buf, start + len, count - len);
    -        }
    -      }
    -    } else {
    -      lastRead = socket.readBytes(buf, start, count);
    -    }
    -    bytesRead += (lastRead == IOUtils.EOF ? 0 : lastRead);
    -
    +    int bytesRead = contentStream.readBytes(buf, start, count);
         contentRead += (bytesRead == IOUtils.EOF ? 0 : bytesRead);
    -    return (bytesRead == 0 && lastRead == IOUtils.EOF) ? IOUtils.EOF : bytesRead;
    +    return bytesRead;
       }
     
       @Override
    @@ -661,7 +663,7 @@ public int writeBytes(byte buf[], int start, int count) throws totalcross.io.IOE
     
       @Override
       public void close() throws totalcross.io.IOException {
    -    socket.close();
    +    contentStream.close();
       }
     
       /**
    @@ -675,6 +677,7 @@ public void close() throws totalcross.io.IOException {
       protected void init(URI uri, Options options) throws totalcross.net.UnknownHostException, totalcross.io.IOException {
         int port;
         String strUri;
    +    final boolean isHttps = "https".equals(uri.scheme.toString());
     
         this.uri = uri;
     
    @@ -694,14 +697,19 @@ protected void init(URI uri, Options options) throws totalcross.net.UnknownHostE
         buffer = new byte[BUFSIZE];
     
         if (port <= 0) {
    -      if (uri.scheme.toString().equals("https")) {
    -        port = 443;
    -      } else {
    -        port = 80;
    -      }
    +      port = isHttps ? 443 : 80;
         }
         state = -1;
     
    +    if (options.socketFactory == null) {
    +      try {
    +        options.socketFactory = isHttps ? SSLContext.getDefault().getSocketFactory()
    +            : SocketFactory.getDefault();
    +      } catch (NoSuchAlgorithmException e) {
    +        throw new IOException(e);
    +      }
    +    }
    +
         socket = options.socketFactory.createSocket(strUri, port, options.openTimeOut);
         socket.readTimeout = options.readTimeOut;
         socket.writeTimeout = options.writeTimeOut == -1 ? options.readTimeOut : options.writeTimeOut;
    @@ -821,6 +829,11 @@ private void getResponse(Options options) throws totalcross.io.IOException {
             options.postHeaders.put("Content-Length", Convert.toString(len));
           }
         }
    +
    +    if (!options.postHeaders.exists("Accept-Encoding") && !options.disableEncoding) {
    +      options.postHeaders.put("Accept-Encoding", "deflate;q=1.0, gzip;q=0.5"); // deflate is preferred for being faster and smaller than gzip
    +    }
    +
         // get the post headers in a single string
         options.postHeaders.dumpKeysValues(sb, ": ", Convert.CRLF); //flsobral@tc126: send post headers also on GET. Temporary fix to fix proxy and authorization support for both GET and POST.
         sb.append(Convert.CRLF);
    @@ -852,6 +865,15 @@ private void getResponse(Options options) throws totalcross.io.IOException {
         if (state != 6 && Settings.onJavaSE) {
           Vm.debug("HTTP: " + getStatus()); // flsobral@tc110_95: No longer stop reading the header when a bad response code is found, so we can get the error cause.
         }
    +
    +    contentStream = new PrefixedStream(buffer, ofsStart, ofsEnd, socket);
    +    if (contentEncoding != null) {
    +        if ("deflate".equalsIgnoreCase(contentEncoding)) {
    +          contentStream = new ZLibStream(contentStream, CompressedStream.INFLATE);
    +        } else if ("gzip".equalsIgnoreCase(contentEncoding)) {
    +          contentStream = new GZipStream(contentStream, CompressedStream.INFLATE); // flsobral@tc112_35: Better performance with GZipStream instead of GZip.
    +        }
    +    }
       }
     
       protected boolean shouldSendData(Options options) {
    @@ -1261,4 +1283,57 @@ public String[] readTokens() throws totalcross.io.IOException // guich@tc125_16
         }
         return tr.readTokens();
       }
    +
    +  private static class PrefixedStream extends Stream {
    +
    +    private byte[] prefix;
    +    private int prefixStart;
    +    private int prefixEnd;
    +    private Stream s;
    +
    +    PrefixedStream(byte[] prefix, int start, int end, Stream s) {
    +      this.prefix = prefix;
    +      this.prefixStart = start;
    +      this.prefixEnd = end;
    +      this.s = s;
    +    }
    +
    +    @Override
    +    public int readBytes(byte[] buf, int start, int count) throws IOException {
    +      int lastRead = IOUtils.EOF;
    +      int bytesRead = 0;
    +  
    +      if (prefixStart < prefixEnd) {
    +        int len = prefixEnd - prefixStart;
    +        if (count < len) {
    +          Vm.arrayCopy(prefix, prefixStart, buf, start, count);
    +          prefixStart += count;
    +          bytesRead = count;
    +        } else {
    +          Vm.arrayCopy(prefix, prefixStart, buf, start, len);
    +          prefixStart = prefixEnd;
    +          prefix = null;
    +          bytesRead = len;
    +          if (count > len) {
    +            lastRead = s.readBytes(buf, start + len, count - len);
    +          }
    +        }
    +      } else {
    +        lastRead = s.readBytes(buf, start, count);
    +      }
    +      bytesRead += (lastRead == IOUtils.EOF ? 0 : lastRead);
    +  
    +      return (bytesRead == 0 && lastRead == IOUtils.EOF) ? IOUtils.EOF : bytesRead;
    +    }
    +
    +    @Override
    +    public int writeBytes(byte[] buf, int start, int count) throws IOException {
    +      return s.writeBytes(buf, start, count);
    +    }
    +
    +    @Override
    +    public void close() throws IOException {
    +      s.close();
    +    }
    +  }
     }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/Socket4D.java b/TotalCrossSDK/src/main/java/totalcross/net/Socket4D.java
    index 812d2bb13b..162d92c12c 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/net/Socket4D.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/Socket4D.java
    @@ -87,17 +87,7 @@ public int readBytes(byte buf[], int start, int count) throws totalcross.io.IOEx
       }
     
       public int readBytes(byte buf[]) throws totalcross.io.IOException {
    -    if (socketRef == null) {
    -      throw new totalcross.io.IOException("The socket is closed.");
    -    }
    -    if (buf == null) {
    -      throw new java.lang.NullPointerException();
    -    }
    -    if (buf.length == 0) {
    -      return 0;
    -    }
    -
    -    return readWriteBytes(buf, 0, buf.length, true);
    +    return readBytes(buf, 0, buf.length);
       }
     
       @Override
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContext.java b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContext.java
    new file mode 100644
    index 0000000000..7ecefc4f3c
    --- /dev/null
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContext.java
    @@ -0,0 +1,71 @@
    +/*********************************************************************************
    + *  TotalCross Software Development Kit                                          *
    + *  Copyright (C) 2000-2012 SuperWaba Ltda.                                      *
    + *  All Rights Reserved                                                          *
    + *                                                                               *
    + *  This library and virtual machine is distributed in the hope that it will     *
    + *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    *
    + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                         *
    + *                                                                               *
    + *  This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0    *
    + *  A copy of this license is located in file license.txt at the root of this    *
    + *  SDK or can be downloaded here:                                               *
    + *  http://www.gnu.org/licenses/lgpl-3.0.txt                                     *
    + *                                                                               *
    + *********************************************************************************/
    +
    +package totalcross.net.ssl;
    +
    +import java.lang.reflect.InvocationTargetException;
    +import java.security.NoSuchAlgorithmException;
    +import java.util.HashMap;
    +import java.util.Map;
    +
    +import totalcross.sys.Settings;
    +
    +public class SSLContext {
    +
    +    private static final Map> map = new HashMap<>();
    +
    +    private static final Class DEFAULT_PROVIDER;
    +
    +    protected final SSLContextSpi sslContextSpi;
    +
    +    static {
    +        map.put("base", SSLContextBase.class);
    +        if (!Settings.onJavaSE && !Settings.isWindowsCE()) {
    +            map.put("mbedtls", SSLContextMbedtls.class);
    +            DEFAULT_PROVIDER = map.get("mbedtls");
    +        } else {
    +            DEFAULT_PROVIDER = map.get("base");
    +        }
    +    }
    +
    +    protected SSLContext(Class sslContextSpi) throws NoSuchAlgorithmException {
    +        try {
    +            this.sslContextSpi = sslContextSpi.getDeclaredConstructor().newInstance();
    +        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
    +                | NoSuchMethodException | SecurityException e) {
    +            throw new NoSuchAlgorithmException(e);
    +        }
    +    }
    +
    +    public static SSLContext getDefault() throws NoSuchAlgorithmException {
    +        return new SSLContext(DEFAULT_PROVIDER);
    +    }
    +
    +    public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException {
    +        // check if protocol is supported
    +        return new SSLContext(DEFAULT_PROVIDER);
    +    }
    +
    +    public static SSLContext getInstance(String protocol, String provider) throws NoSuchAlgorithmException {
    +        Class spi = map.get(provider);
    +        // check if protocol is supported
    +        return new SSLContext(spi);
    +    }
    +
    +    public SSLSocketFactory getSocketFactory() {
    +        return sslContextSpi.getSocketFactory();
    +    }
    +}
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextBase.java b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextBase.java
    new file mode 100644
    index 0000000000..3b4f5caa53
    --- /dev/null
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextBase.java
    @@ -0,0 +1,121 @@
    +/*********************************************************************************
    + *  TotalCross Software Development Kit                                          *
    + *  Copyright (C) 2000-2012 SuperWaba Ltda.                                      *
    + *  All Rights Reserved                                                          *
    + *                                                                               *
    + *  This library and virtual machine is distributed in the hope that it will     *
    + *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    *
    + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                         *
    + *                                                                               *
    + *  This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0    *
    + *  A copy of this license is located in file license.txt at the root of this    *
    + *  SDK or can be downloaded here:                                               *
    + *  http://www.gnu.org/licenses/lgpl-3.0.txt                                     *
    + *                                                                               *
    + *********************************************************************************/
    +
    +package totalcross.net.ssl;
    +
    +import totalcross.crypto.CryptoException;
    +import totalcross.io.ByteArrayStream;
    +import totalcross.io.IOException;
    +import totalcross.net.Socket;
    +import totalcross.sys.Vm;
    +
    +public class SSLContextBase extends SSLContextSpi {
    +
    +    private SSLClient sslClient;
    +    private SSL sslConnection;
    +    private SSLReadHolder sslReader;
    +    private ByteArrayStream buffer = null;
    +
    +    protected SSLContextBase() {
    +        super("base");
    +    }
    +
    +    @Override
    +    void init(Socket socket) throws IOException {
    +        // startHandshake(socket);
    +    }
    +
    +    protected SSLClient prepareContext() throws CryptoException {
    +        return new SSLClient(Constants.SSL_SERVER_VERIFY_LATER, 0);
    +    }
    +
    +    @Override
    +    void startHandshake(Socket socket) throws IOException {
    +        try {
    +            sslClient = prepareContext();
    +            sslConnection = sslClient.connect(socket, null);
    +            Exception e = sslConnection.getLastException();
    +            if (e != null) {
    +                throw new IOException(e.getMessage());
    +            }
    +            int status;
    +            for (int elapsedTime = 0; (status = sslConnection.handshakeStatus()) == Constants.SSL_HANDSHAKE_IN_PROGRESS
    +                    && elapsedTime < socket.readTimeout; elapsedTime += 25) {
    +                Vm.sleep(25);
    +            }
    +            if (status != Constants.SSL_OK) {
    +                throw new IOException("SSL handshake failed: " + status);
    +            }
    +            sslReader = new SSLReadHolder();
    +            buffer = new ByteArrayStream(256);
    +            buffer.mark();
    +        } catch (Exception e) {
    +            try {
    +                socket.close();
    +            } catch (IOException e2) {
    +            }
    +            if (e instanceof IOException) {
    +                throw (IOException) e;
    +            }
    +            throw new IOException(e.getMessage());
    +        }
    +    }
    +
    +    @Override
    +    int readWriteBytes(Socket socket, byte[] buf, int start, int count, boolean isRead) throws IOException {
    +        if (isRead) {
    +            if (buffer == null) {
    +                return socket.readBytes(buf, start, count);
    +            }
    +            if (buffer.available() == 0) {
    +                int sslReadBytes = sslConnection.read(sslReader);
    +                buffer.reuse();
    +                if (sslReadBytes > 0) {
    +                    buffer.writeBytes(sslReader.getData(), 0, sslReadBytes);
    +                }
    +                buffer.mark();
    +            }
    +            int readBytes = buffer.readBytes(buf, start, count);
    +
    +            return readBytes;
    +        } else {
    +            if (buffer == null) {
    +                return socket.writeBytes(buf, start, count);
    +            }
    +            if (start > 0) {
    +                byte[] buf2 = new byte[count];
    +                Vm.arrayCopy(buf, start, buf2, 0, count);
    +                buf = buf2;
    +            }
    +            return sslConnection.write(buf, count);
    +        }
    +    }
    +
    +    @Override
    +    void close() throws IOException {
    +        if (buffer != null) {
    +            buffer = null;
    +        }
    +        if (sslConnection != null) {
    +            sslConnection.dispose();
    +            sslConnection = null;
    +        }
    +        if (sslClient != null) {
    +            sslClient.dispose();
    +            sslClient = null;
    +        }
    +    }
    +}
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextMbedtls.java b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextMbedtls.java
    new file mode 100644
    index 0000000000..5e8d12db57
    --- /dev/null
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextMbedtls.java
    @@ -0,0 +1,61 @@
    +/*********************************************************************************
    + *  TotalCross Software Development Kit                                          *
    + *  Copyright (C) 2000-2012 SuperWaba Ltda.                                      *
    + *  All Rights Reserved                                                          *
    + *                                                                               *
    + *  This library and virtual machine is distributed in the hope that it will     *
    + *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    *
    + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                         *
    + *                                                                               *
    + *  This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0    *
    + *  A copy of this license is located in file license.txt at the root of this    *
    + *  SDK or can be downloaded here:                                               *
    + *  http://www.gnu.org/licenses/lgpl-3.0.txt                                     *
    + *                                                                               *
    + *********************************************************************************/
    +
    +package totalcross.net.ssl;
    +
    +import totalcross.io.IOException;
    +import totalcross.net.Socket;
    +
    +public class SSLContextMbedtls extends SSLContextSpi {
    +
    +    @SuppressWarnings("unused")
    +    private byte[] context;
    +
    +    @SuppressWarnings("unused")
    +    private byte[] mbedtls_net_context;
    +
    +    @SuppressWarnings("unused")
    +    private byte[] mbedtls_entropy_context;
    +
    +    @SuppressWarnings("unused")
    +    private byte[] mbedtls_ctr_drbg_context;
    +
    +    @SuppressWarnings("unused")
    +    private byte[] mbedtls_ssl_context;
    +
    +    @SuppressWarnings("unused")
    +    private byte[] mbedtls_ssl_config;
    +
    +    protected SSLContextMbedtls() {
    +        super("mbedtls");
    +    }
    +
    +    @Override
    +    native void init(Socket socket);
    +
    +    @Override
    +    native int readWriteBytes(Socket socket, byte[] buf, int start, int count, boolean isRead) throws IOException;
    +
    +    @Override
    +    void startHandshake(Socket socket) throws IOException {
    +        // TODO Auto-generated method stub
    +        // throw new UnsupportedOperationException("Unimplemented method
    +        // 'startHandshake'");
    +    }
    +
    +    @Override
    +    native void close() throws IOException;
    +}
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextSpi.java b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextSpi.java
    new file mode 100644
    index 0000000000..54978678ab
    --- /dev/null
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLContextSpi.java
    @@ -0,0 +1,47 @@
    +/*********************************************************************************
    + *  TotalCross Software Development Kit                                          *
    + *  Copyright (C) 2000-2012 SuperWaba Ltda.                                      *
    + *  All Rights Reserved                                                          *
    + *                                                                               *
    + *  This library and virtual machine is distributed in the hope that it will     *
    + *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    *
    + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                         *
    + *                                                                               *
    + *  This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0    *
    + *  A copy of this license is located in file license.txt at the root of this    *
    + *  SDK or can be downloaded here:                                               *
    + *  http://www.gnu.org/licenses/lgpl-3.0.txt                                     *
    + *                                                                               *
    + *********************************************************************************/
    +
    +package totalcross.net.ssl;
    +
    +import totalcross.io.IOException;
    +import totalcross.net.Socket;
    +import totalcross.net.UnknownHostException;
    +
    +public abstract class SSLContextSpi {
    +    protected final String name;
    +
    +    protected SSLContextSpi(String name) {
    +        this.name = name;
    +    }
    +
    +    private final SSLSocketFactory factory = new SSLSocketFactory() {
    +        public Socket createSocket(String host, int port, int timeout) throws UnknownHostException, IOException {
    +            return new SSLSocket(SSLContextSpi.this, host, port, timeout);
    +        }
    +    };
    +
    +    public SSLSocketFactory getSocketFactory() {
    +        return factory;
    +    }
    +
    +    abstract void init(Socket socket) throws IOException;
    +
    +    abstract void startHandshake(Socket socket) throws IOException;
    +
    +    abstract int readWriteBytes(Socket socket, byte[] buf, int start, int count, boolean isRead) throws IOException;
    +
    +    abstract void close() throws IOException;
    +}
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocket.java b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocket.java
    index 66e1bf1bdb..75c2524ad6 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocket.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocket.java
    @@ -6,11 +6,9 @@
     package totalcross.net.ssl;
     
     import totalcross.crypto.CryptoException;
    -import totalcross.io.ByteArrayStream;
     import totalcross.io.IOException;
     import totalcross.net.Socket;
     import totalcross.net.UnknownHostException;
    -import totalcross.sys.Vm;
     
     /**
      * This class extends Sockets and provides secure socket using protocols such as the "Secure Sockets Layer" (SSL) or
    @@ -20,10 +18,7 @@
      * If handshaking fails for any reason, the SSLSocket is closed, and no further communications can be done.
      */
     public class SSLSocket extends Socket {
    -  private SSLClient sslClient;
    -  private SSL sslConnection;
    -  private SSLReadHolder sslReader;
    -  private ByteArrayStream buffer = null;
    +  SSLContextSpi sslContext;
     
       /**
        * Constructs an SSL connection to a named host at a specified port, with the specified connection timeout, binding
    @@ -40,8 +35,10 @@ public class SSLSocket extends Socket {
        * @throws IOException
        *            if an I/O error occurs when creating the socket
        */
    -  public SSLSocket(String host, int port, int timeout) throws UnknownHostException, IOException {
    +  protected SSLSocket(SSLContextSpi sslContext, String host, int port, int timeout) throws UnknownHostException, IOException {
         super(host, port, timeout);
    +    this.sslContext = sslContext;
    +    this.sslContext.init(this);
       }
     
       /**
    @@ -63,83 +60,42 @@ protected SSLClient prepareContext() throws CryptoException {
        *            on a network level error
        */
       public void startHandshake() throws IOException {
    -    try {
    -      sslClient = prepareContext();
    -      sslConnection = sslClient.connect(this, null);
    -      Exception e = sslConnection.getLastException();
    -      if (e != null) {
    -        throw new IOException(e.getMessage());
    -      }
    -      int status;
    -      for (int elapsedTime = 0; (status = sslConnection.handshakeStatus()) == Constants.SSL_HANDSHAKE_IN_PROGRESS
    -          && elapsedTime < super.readTimeout; elapsedTime += 25) {
    -        Vm.sleep(25);
    -      }
    -      if (status != Constants.SSL_OK) {
    -        throw new IOException("SSL handshake failed: " + status);
    -      }
    -      sslReader = new SSLReadHolder();
    -      buffer = new ByteArrayStream(256);
    -      buffer.mark();
    -    } catch (Exception e) {
    -      try {
    -        this.close();
    -      } catch (IOException e2) {
    -      }
    -      if (e instanceof IOException) {
    -        throw (IOException) e;
    -      }
    -      throw new IOException(e.getMessage());
    -    }
    +    this.sslContext.startHandshake(this);
       }
     
       @Override
       public int readBytes(byte[] buf, int start, int count) throws IOException {
    -    if (buffer == null) {
    -      return super.readBytes(buf, start, count);
    +    if (buf == null) {
    +      throw new NullPointerException();
         }
    -    if (buffer.available() == 0) {
    -      int sslReadBytes = sslConnection.read(sslReader);
    -      buffer.reuse();
    -      if (sslReadBytes > 0) {
    -        buffer.writeBytes(sslReader.getData(), 0, sslReadBytes);
    -      }
    -      buffer.mark();
    +    if (start < 0 || count < 0 || start + count > buf.length) {
    +      throw new IndexOutOfBoundsException();
    +    }
    +    if (count == 0) {
    +      return 0;
         }
    -    int readBytes = buffer.readBytes(buf, start, count);
    -
    -    return readBytes;
    -  }
     
    -  @Override
    -  public int readBytes(byte[] buf) throws IOException {
    -    return this.readBytes(buf, 0, buf.length);
    +    return sslContext.readWriteBytes(this, buf, start, count, true);
       }
     
       @Override
       public int writeBytes(byte[] buf, int start, int count) throws IOException {
    -    if (buffer == null) {
    -      return super.writeBytes(buf, start, count);
    +    if (buf == null) {
    +      throw new NullPointerException();
         }
    -    if (start > 0) {
    -      byte[] buf2 = new byte[count];
    -      Vm.arrayCopy(buf, start, buf2, 0, count);
    -      buf = buf2;
    +    if (start < 0 || count < 0 || start + count > buf.length) {
    +      throw new IndexOutOfBoundsException();
         }
    -    return sslConnection.write(buf, count);
    +    if (count == 0) {
    +      return 0;
    +    }
    +
    +    return sslContext.readWriteBytes(this, buf, start, count, false);
       }
     
       @Override
       public void close() throws IOException {
    -    if (buffer != null) {
    -      buffer = null;
    -    }
    -    if (sslConnection != null) {
    -      sslConnection.dispose();
    -    }
    -    if (sslClient != null) {
    -      sslClient.dispose();
    -    }
    +    this.sslContext.close();
         super.close();
       }
     }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocketFactory.java b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocketFactory.java
    index 11369e48d5..337dfed8a4 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocketFactory.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/net/ssl/SSLSocketFactory.java
    @@ -13,15 +13,7 @@
     /**
      * SSLSocketFactory creates SSLSockets.
      */
    -public class SSLSocketFactory extends SocketFactory {
    -	private static SSLSocketFactory instance;
    -
    -	public static SocketFactory getDefault() {
    -		if (instance == null) {
    -			instance = new SSLSocketFactory();
    -		}
    -		return instance;
    -	}
    +public abstract class SSLSocketFactory extends SocketFactory {
     
     	@Override
     	public Socket createSocket(String host, int port) throws UnknownHostException, IOException {
    @@ -29,7 +21,5 @@ public Socket createSocket(String host, int port) throws UnknownHostException, I
     	}
     
     	@Override
    -  public Socket createSocket(String host, int port, int timeout) throws UnknownHostException, IOException {
    -		return new SSLSocket(host, port, timeout);
    -	}
    +	public abstract Socket createSocket(String host, int port, int timeout) throws UnknownHostException, IOException;
     }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/phone/Dial.java b/TotalCrossSDK/src/main/java/totalcross/phone/Dial.java
    index edd7695423..7f44ff8142 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/phone/Dial.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/phone/Dial.java
    @@ -10,7 +10,7 @@
     
     /** Used to dial a number in a smartphone.
      * A single listener can receive messages from the system informing the current status.
    - * Currently works on Android, and iOS.
    + * Currently works on Android and iOS.
      *
      * @since TotalCross 1.0
      */
    diff --git a/TotalCrossSDK/src/main/java/totalcross/sql/Connection.java b/TotalCrossSDK/src/main/java/totalcross/sql/Connection.java
    index e54fa05dbc..0d77db6092 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/sql/Connection.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/sql/Connection.java
    @@ -19,7 +19,7 @@ public interface Connection extends AutoCloseable {
     
       public PreparedStatement prepareStatement(String sql) throws SQLException;
     
    -  public String nativeSQL(String sql);
    +  public String nativeSQL(String sql) throws SQLException;
     
       public void setAutoCommit(boolean autoCommit) throws SQLException;
     
    diff --git a/TotalCrossSDK/src/main/java/totalcross/sql/DriverManager.java b/TotalCrossSDK/src/main/java/totalcross/sql/DriverManager.java
    index c9594e7cc4..f698bc8170 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/sql/DriverManager.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/sql/DriverManager.java
    @@ -1,12 +1,15 @@
     package totalcross.sql;
     
     import java.sql.SQLException;
    +
     import totalcross.sql.sqlite4j.SQLite4JConnection;
     import totalcross.sys.Convert;
     import totalcross.sys.Settings;
     import totalcross.sys.Vm;
    +import totalcross.util.Hashtable;
     import totalcross.util.Logger;
     import totalcross.util.Properties;
    +import totalcross.util.Vector;
     
     public class DriverManager {
       public static Logger getLogWriter() {
    @@ -17,8 +20,32 @@ public static void setLogWriter(Logger out) {
       }
     
       public static Connection getConnection(String url, Properties info) throws SQLException {
    -    return null;
    -  }
    +      if (url.startsWith("jdbc:sqlite:")) // :sample.db
    +      {
    +        int l = url.length();
    +        String dbname = l == 12 ? "temp.db" : url.substring(12);
    +        if ((Settings.isIOS() || Settings.platform.equals(Settings.ANDROID)) && dbname.indexOf("/") == -1
    +            && dbname.indexOf(":memory:") == -1 && dbname.indexOf("mode=memory") == -1) // dont use this for memory databases
    +        {
    +          // in ios and android its required that the user specify a valid path. if he don't, we put the database in the app path
    +          boolean isfile = dbname.startsWith("file:");
    +          if (isfile) {
    +            dbname = dbname.substring(5);
    +          }
    +          dbname = Convert.appendPath(Settings.appPath, dbname);
    +          if (isfile) {
    +            dbname = "file:".concat(dbname);
    +          }
    +          Vm.debug("changing dbname to " + dbname);
    +        }
    +        try {
    +          return newConnection(url, dbname, info);
    +        } catch (java.sql.SQLException e) {
    +          throw new SQLException("Can't get connection: " + url + initCause(e));
    +        }
    +      }
    +      return null;
    +    }
     
       public static Connection getConnection(String url, String user, String password) throws SQLException {
         return null;
    @@ -29,42 +56,41 @@ static String initCause(Throwable e) {
       }
     
       public static Connection getConnection(String url) throws SQLException {
    -    if (url.startsWith("jdbc:sqlite:")) // :sample.db
    -    {
    -      int l = url.length();
    -      String dbname = l == 12 ? "temp.db" : url.substring(12);
    -      if ((Settings.isIOS() || Settings.platform.equals(Settings.ANDROID)) && dbname.indexOf("/") == -1
    -          && dbname.indexOf(":memory:") == -1 && dbname.indexOf("mode=memory") == -1) // dont use this for memory databases
    -      {
    -        // in ios and android its required that the user specify a valid path. if he don't, we put the database in the app path
    -        boolean isfile = dbname.startsWith("file:");
    -        if (isfile) {
    -          dbname = dbname.substring(5);
    +      return getConnection(url, null);
    +  }
    +
    +    static Connection newConnection(String url, String dbname, Properties props) throws SQLException {
    +        java.util.Properties props2 = new java.util.Properties();
    +        if (props != null) {
    +            Vector keys = props.getKeys();
    +            for (int i = keys.size() - 1; i >= 0; i--) {
    +                String key = (String) keys.items[i];
    +                props2.put(key, props.get(key).toString());
    +            }
             }
    -        dbname = Convert.appendPath(Settings.appPath, dbname);
    -        if (isfile) {
    -          dbname = "file:".concat(dbname);
    +        props2.put("date_string_format", "yyyy-MM-dd");
    +        return new SQLite4JConnection(new org.sqlite.jdbc4.JDBC4Connection(url, dbname, props2));
    +    }
    +
    +    static Connection newConnection4D(String url, String dbname, Properties props) throws SQLException {
    +        Hashtable props2 = new Hashtable(10);
    +        if (props != null) {
    +            Vector keys = props.getKeys();
    +            for (int i = keys.size() - 1; i >= 0; i--) {
    +                String key = (String) keys.items[i];
    +                props2.put(key, props.get(key).toString());
    +            }
             }
    -        Vm.debug("changing dbname to " + dbname);
    -      }
    -      try {
    -        return newConnection(url, dbname);
    -      } catch (java.sql.SQLException e) {
    -        throw new SQLException("Can't get connection: " + url + initCause(e));
    -      }
    +        return new totalcross.db.sqlite.SQLiteConnection(url, dbname, props2);
         }
    -    return null;
    -  }
     
    -  static Connection newConnection(String url, String dbname) throws SQLException {
    -    java.util.Properties props = new java.util.Properties();
    -    props.put("date_string_format", "yyyy-MM-dd");
    -    return new SQLite4JConnection(new org.sqlite.SQLiteConnection(url, dbname, props));
    -  }
    +    static Connection newConnection(String url, String dbname) throws SQLException {
    +        return newConnection(url, dbname, null);
    +    }
     
    -  static Connection newConnection4D(String url, String dbname) throws SQLException {
    -    return new totalcross.db.sqlite.SQLiteConnection(url, dbname);
    -  }
    +    static Connection newConnection4D(String url, String dbname) throws SQLException {
    +        return newConnection4D(url, dbname, null);
    +    }
     
       public static Driver getDriver(String url) throws SQLException {
         return null;
    diff --git a/TotalCrossSDK/src/main/java/totalcross/sql/sqlite4j/SQLite4JConnection.java b/TotalCrossSDK/src/main/java/totalcross/sql/sqlite4j/SQLite4JConnection.java
    index 5f48acc7dd..e0a9a4b444 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/sql/sqlite4j/SQLite4JConnection.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/sql/sqlite4j/SQLite4JConnection.java
    @@ -30,7 +30,7 @@ public PreparedStatement prepareStatement(String sql) throws SQLException {
       }
     
       @Override
    -  public String nativeSQL(String sql) {
    +  public String nativeSQL(String sql) throws SQLException {
         return con.nativeSQL(sql);
       }
     
    diff --git a/TotalCrossSDK/src/main/java/totalcross/sys/Convert.java b/TotalCrossSDK/src/main/java/totalcross/sys/Convert.java
    index 6611b96c6d..3b92da1fdf 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/sys/Convert.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/sys/Convert.java
    @@ -2285,36 +2285,21 @@ public static boolean isLowerCase(char c) // guich@tc113_4
        * @since TotalCross 1.2
        */
       public static double getDistancePoint2Rect(int x, int y, int x1, int y1, int x2, int y2) {
    -    int xMx1 = x - x1;
    -    int xMx2 = x - x2;
    -    int yMy1 = y - y1;
    -    int yMy2 = y - y2;
    -
    -    if (y < y1) {
    -      if (x < x1) {
    -        return Math.sqrt(xMx1 * xMx1 + yMy1 * yMy1); // distance from point (x,y) to point (x1,y1)
    -      } else if (x < x2) {
    -        return y1 - y; // distance from point (x,y) to line (x1,y1)->(x2,y1)
    -      } else {
    -        return Math.sqrt(xMx2 * xMx2 + yMy1 * yMy1); // distance from point (x,y) to point (x2,y1)
    -      }
    -    } else if (y < y2) {
    -      if (x < x1) {
    -        return x1 - x; // distance from point (x,y) to line (x1,y1)->(x1,y2)
    -      } else if (x < x2) {
    -        return 0;
    -      } else {
    -        return x - x2; // distance from point (x,y) to line (x2,y1)->(x2,y2)
    -      }
    -    } else {
    -      if (x < x1) {
    -        return Math.sqrt(xMx1 * xMx1 + yMy2 * yMy2); // distance from point (x,y) to point (x1,y2)
    -      } else if (x < x2) {
    -        return y - y2; // distance from point (x,y) to line (x1,y2)->(x2,y2)
    -      } else {
    -        return Math.sqrt(xMx2 * xMx2 + yMy2 * yMy2); // distance from point (x,y) to point (x2,y2)
    -      }
    -    }
    +    if (x1 > x2) { int t = x1; x1 = x2; x2 = t; }
    +    if (y1 > y2) { int t = y1; y1 = y2; y2 = t; }
    +
    +    /* 
    +      Code below equivalent to:
    +      double dx = Math.max(Math.max(x1 - x, 0), x - x2);
    +     */
    +    double dx = (x < x1) ? (x1 - x) : (x > x2 ? x - x2 : 0);
    +    /* 
    +      Code below equivalent to:
    +      double dy = Math.max(Math.max(y1 - y, 0), y - y2);
    +     */
    +    double dy = (y < y1) ? (y1 - y) : (y > y2 ? y - y2 : 0);
    +
    +    return Math.hypot(dx, dy);
       }
     
       /** Appends the given timestamp to a StringBuffer, using the current Date format and separators.
    diff --git a/TotalCrossSDK/src/main/java/totalcross/sys/Settings.java b/TotalCrossSDK/src/main/java/totalcross/sys/Settings.java
    index daa56c1d4f..ff75115933 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/sys/Settings.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/sys/Settings.java
    @@ -6,6 +6,7 @@
     package totalcross.sys;
     
     import java.io.File;
    +import java.util.HashMap;
     
     import com.totalcross.annotations.ReplacedByNativeOnDeploy;
     import totalcross.ui.IVirtualKeyboard;
    @@ -22,10 +23,10 @@ public final class Settings {
        * base 100. For example, version 1.0 has value 100. version 4 has a
        * version value of 400. A beta 0.81 VM will have version 81.
        */
    -  public static int version = 710;
    +  public static int version = 720;
     
       /** Field that represents the version in a string form, like "1.36". Only digits and dot is allowed or an exception will be throws during tc.Deploy. */
    -  public static String versionStr = "7.1.0";
    +  public static String versionStr = "7.2.0";
     
       /** Current build number for the TotalCross SDK.
        * @since TotalCross 1.53 
    @@ -526,28 +527,32 @@ static String initAppPath4D() {
        */
       public static String appVersion;
     
    -  /** Set it at the application's static initializer; should not contain spaces. Defines the company's information, which is used in iOS, Windows CE.
    +  /** Set it at the application's static initializer; should not contain spaces. Defines the company's information, which is used in iOS and Windows CE.
        * In iOS is used to form the bundle suffix id. 
        * @since TotalCross 1.0
        */
       public static String companyInfo;
     
    -  /** Set it at the application's static initializer. Defines the user's or company's contact email.
    +  /** Set it at the application's static initializer. Defines the user's or company's contact email, which was used in WP8.
        * @since TotalCross 1.0
        */
    +  @Deprecated
       public static String companyContact;
     
    -  /** Set it at the application's static initializer. Defines the application's description.
    +  /** Set it at the application's static initializer. Defines the application's description, which was used in WP8.
        * @since TotalCross 1.0
        */
    +  @Deprecated
       public static String appDescription;
     
    -  /** Set it at the application's static initializer. Defines the application's package identifier.
    +  /** Set it at the application's static initializer. Defines the application's package identifier, which was used in WP8.
        */
    +  @Deprecated
       public static String appPackageIdentifier;
     
    -  /** Set it at the application's static initializer. Defines the application's package publisher.
    +  /** Set it at the application's static initializer. Defines the application's package publisher, which was used in WP8.
        */
    +  @Deprecated
       public static String appPackagePublisher;
     
       /** Set it at the application's static initializer. Defines the application's package id, which is used in iOS. */
    @@ -840,7 +845,7 @@ public void setRect(int x, int y, int w, int h)
        * on top of the Edit.
        * You must set this in the MainWindow's constructor, never in the static block.
        * @see #SIPBottomLimit
    -   * @see totalcross.ui.UIColors#shiftScreenColor
    +   * @see totalcross.ui.UIColors#unsafeAreaColor
        * @since TotalCross 1.3
        */
       public static boolean unmovableSIP; // guich@tc126_21
    @@ -1054,7 +1059,23 @@ private Settings() {
     
       /** Set to false to disable UI positional errors that are shown in Java SE */
       public static boolean showUIErrors = true;
    -  
    +
    +  private static final HashMap keyMap = new HashMap<>();
    +
    +  /**
    +   * Returns an id from the mobile device (Android and iOS) that should be
    +   * persistent on reinstall.
    +   * 
    +   * 
      + *
    • Android - Uses UUID with MediaDrm to generate an ID.
    • + *
    • iOS - Retrieves value from UIDevice.identifierForVendor + * (https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor)
    • + *
    + */ + public static String getUniqueId() { + return keyMap.getOrDefault("device.id", "NOT_SUPPORTED"); + } + /** * On Android it has the value of Settings.Secure.ANDROID_ID. * Refer to the Android documentation for more details about this identifier: diff --git a/TotalCrossSDK/src/main/java/totalcross/sys/UTF8CharacterConverter.java b/TotalCrossSDK/src/main/java/totalcross/sys/UTF8CharacterConverter.java index 41a5bae10a..f4d878ea50 100644 --- a/TotalCrossSDK/src/main/java/totalcross/sys/UTF8CharacterConverter.java +++ b/TotalCrossSDK/src/main/java/totalcross/sys/UTF8CharacterConverter.java @@ -22,6 +22,8 @@ */ public class UTF8CharacterConverter extends AbstractCharacterConverter { + public static char REPLACEMENT_CHARACTER = '\uFFFD'; + protected UTF8CharacterConverter() { super("UTF-8", new String[] { "unicode-1-1-utf-8", @@ -53,14 +55,14 @@ public char[] bytes2chars(byte bytes[], int start, int length) { } if (start >= end) // If no byte follows, { - chars[tgtOfs++] = '?'; // set MCS + chars[tgtOfs++] = REPLACEMENT_CHARACTER; // set MCS break; // done } int c = (bytes[start++] & 0xFF) ^ 0x80; // 2nd byte if ((c & 0xC0) != 0) // starts new sequence? { --start; // Yes, backup - chars[tgtOfs++] = '?'; // set MCS + chars[tgtOfs++] = REPLACEMENT_CHARACTER; // set MCS continue; // pursue } int r = (c0 << 6) | c; // Get encoded value @@ -71,14 +73,14 @@ public char[] bytes2chars(byte bytes[], int start, int length) { } if (start >= end) // If no byte follows, { - chars[tgtOfs++] = '?'; // set MCS + chars[tgtOfs++] = REPLACEMENT_CHARACTER; // set MCS break; // done } c = (bytes[start++] & 0xFF) ^ 0x80; // 3rd byte if ((c & 0xC0) != 0) // starts new sequence? { --start; // Yes, backup - chars[tgtOfs++] = '?'; // set MCS + chars[tgtOfs++] = REPLACEMENT_CHARACTER; // set MCS continue; // pursue } chars[tgtOfs++] = (char) ((r << 6) | c); // Get encoded value diff --git a/TotalCrossSDK/src/main/java/totalcross/sys/Vm4D.java b/TotalCrossSDK/src/main/java/totalcross/sys/Vm4D.java index b8bf6f109b..7d91ee69cb 100644 --- a/TotalCrossSDK/src/main/java/totalcross/sys/Vm4D.java +++ b/TotalCrossSDK/src/main/java/totalcross/sys/Vm4D.java @@ -5,6 +5,14 @@ package totalcross.sys; +import java.io.IOException; + +import totalcross.crypto.CryptoException; +import totalcross.crypto.cipher.AESCipher; +import totalcross.crypto.cipher.AESKey; +import totalcross.crypto.cipher.Cipher; +import totalcross.io.ByteArrayStream; +import totalcross.io.DataStream; import totalcross.ui.Window; import totalcross.util.Hashtable; @@ -143,4 +151,40 @@ public static String getStackTrace() { return getStackTrace(e); } } + + private static final String RESFILE_TCKEY = "tckey.bin"; + + private static byte[] readKey() throws IOException, CryptoException { + // Read encrypted data from the key file + byte[] enc = Vm.getFile(RESFILE_TCKEY); + if (enc == null) { + return null; + } + + // Decrypt data and return + return aesDecrypt(enc); + } + + private static byte[] aesDecrypt(byte[] enc) throws IOException, CryptoException { + AESKey aesKey = new AESKey(getAESKeyData()); + AESCipher aesCipher = new AESCipher(); + ByteArrayStream bas = new ByteArrayStream(enc); + DataStream ds = new DataStream(bas); + + byte[] riv = new byte[ds.readInt()]; + ds.readBytes(riv, 0, riv.length); + byte[] out = new byte[ds.readInt()]; + ds.readBytes(out, 0, out.length); + + aesCipher.reset(Cipher.OPERATION_DECRYPT, aesKey, Cipher.CHAINING_CBC, riv, Cipher.PADDING_PKCS5); + aesCipher.update(out); + + return aesCipher.getOutput(); + } + + private static byte[] getAESKeyData() { + return new byte[] { (byte) 0x06, (byte) 0x05, (byte) 0xF4, (byte) 0xF0, (byte) 0xF4, (byte) 0x08, (byte) 0x01, + (byte) 0x09, (byte) 0xF7, (byte) 0x09, (byte) 0xFE, (byte) 0xFC, (byte) 0xF5, (byte) 0x04, (byte) 0x00, + (byte) 0x0B }; + } } \ No newline at end of file diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/Button.java b/TotalCrossSDK/src/main/java/totalcross/ui/Button.java index 7330b64921..d3fda61a62 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/Button.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/Button.java @@ -893,6 +893,7 @@ public void onPaint(Graphics g) { @Override protected void onFontChanged() { + int oldMaxTW = maxTW; if (text != null) { if (linesW == null || linesW.length != lines.length) { linesW = new int[lines.length]; @@ -904,13 +905,22 @@ protected void onFontChanged() { maxTW = Math.max(maxTW, linesW[i]); } } - onBoundsChanged(false); + if (oldMaxTW != maxTW) { + onBoundsChanged(false); + } + } + + private void invalidateImages() { + if (npback != null) { + npback.freeTexture(); + npback = null; + } } @Override protected void onBoundsChanged(boolean screenChanged) { int tiGap = getGap(this.tiGap); - npback = null; + invalidateImages(); if (imageHeightFactor != 0 && img0 != null) { try { img = img0.hwScaledFixedAspectRatio(height * imageHeightFactor / 100, true); @@ -1054,7 +1064,7 @@ protected void onBoundsChanged(boolean screenChanged) { @Override protected void onColorsChanged(boolean colorsChanged) { boolean enabled = isEnabled(); - npback = null; + invalidateImages(); if (!enabled && autoRepeatTimer != null) { disableAutoRepeat(); } diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/ClippedContainer.java b/TotalCrossSDK/src/main/java/totalcross/ui/ClippedContainer.java index 536a8ad643..a3e7cbfc6b 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/ClippedContainer.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/ClippedContainer.java @@ -64,6 +64,9 @@ public void paintChildren() { // now go forward until no other is visible for (i = first; i < n; i++) { Control child = (Control) items[i]; + if (child == null) { + continue; + } if (painted++ > 0 && !child.isVisibleAndInside(bagClipY0, bagClipYf)) { break; } diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/ComboBox.java b/TotalCrossSDK/src/main/java/totalcross/ui/ComboBox.java index d232b93099..1b4a29a79a 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/ComboBox.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/ComboBox.java @@ -625,7 +625,7 @@ protected void updatePopRect() { private boolean isSupportedListBox() { String cl = pop.lb.getClass().getName(); - return cl.equals("totalcross.ui.ListBox") || cl.equals("litebase.ui.DBListBox"); + return cl.equals("totalcross.ui.ListBox"); } /** Pops up the ComboBoxDropDown */ diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/Container.java b/TotalCrossSDK/src/main/java/totalcross/ui/Container.java index ef89171a3e..03eca01e97 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/Container.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/Container.java @@ -38,7 +38,7 @@ public class Container extends Control { private int pressColor = -1; private PenListener pe; private boolean cpressed; - private Image npback; + protected Image npback; /** Sets the type of background of this Container. To disable the background, set the * transparentBackground of the Control class to true. This field is used when @@ -573,32 +573,36 @@ protected void onColorsChanged(boolean colorsChanged) { } protected void fillBackground(Graphics g, int b) { - if(npParts != null && npback == null) { - try { - npback = NinePatch.getInstance().getNormalInstance(npParts, width, height, b, false); - } catch(ImageException e) { - if(Settings.onJavaSE) - e.printStackTrace(); - } - } else if(npback == null){ - switch (backgroundStyle) { - case BACKGROUND_SOLID: - g.backColor = b; - g.fillRect(0, 0, width, height); - break; - case BACKGROUND_SHADED: - g.fillShadedRect(0, 0, width, height, true, false, foreColor, b, UIColors.shadeFactor); - break; - case BACKGROUND_SHADED_INV: - g.fillShadedRect(0, 0, width, height, false, false, foreColor, b, UIColors.shadeFactor); - break; - case BACKGROUND_CYLINDRIC_SHADED: - g.drawCylindricShade(foreColor, b, 0, 0, width, height); - break; - } - } - - NinePatch.tryDrawImage(g, npback, 0, 0); + fillBackground(g, b, foreColor, 0, 0, width, height); + } + + protected void fillBackground(Graphics g, int backColor, int foreColor, int x, int y, int width, int height) { + if (npParts != null && npback == null) { + try { + npback = NinePatch.getInstance().getNormalInstance(npParts, width, height, backColor, false); + } catch (ImageException e) { + if (Settings.onJavaSE) + e.printStackTrace(); + } + } else if (npback == null) { + switch (backgroundStyle) { + case BACKGROUND_SOLID: + g.backColor = backColor; + g.fillRect(x, y, width, height); + break; + case BACKGROUND_SHADED: + g.fillShadedRect(x, y, width, height, true, false, foreColor, backColor, UIColors.shadeFactor); + break; + case BACKGROUND_SHADED_INV: + g.fillShadedRect(x, y, width, height, false, false, foreColor, backColor, UIColors.shadeFactor); + break; + case BACKGROUND_CYLINDRIC_SHADED: + g.drawCylindricShade(foreColor, backColor, x, y, width, height); + break; + } + } + + NinePatch.tryDrawImage(g, npback, x, y); } /** Draws the border (if any). If you override this method, be sure to call diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/Control.java b/TotalCrossSDK/src/main/java/totalcross/ui/Control.java index c8aa571252..c13a03d18a 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/Control.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/Control.java @@ -768,9 +768,11 @@ public void setRect(int x, int y, int width, int height, Control relative, boole parent.lastY = cli.y; } } else { - cli.y = cli.x = 0; // guich@450a_40 - cli.width = Settings.screenWidth; - cli.height = Settings.screenHeight; + Insets i = Window.getSafeAreaInsets(); + cli.x = i.left; + cli.y = i.top; + cli.width = Settings.screenWidth - i.left - i.right; + cli.height = Settings.screenHeight - i.top - i.bottom; } if (Settings.uiAdjustmentsBasedOnFontHeight && uiAdjustmentsBasedOnFontHeightIsSupported) { diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/Edit.java b/TotalCrossSDK/src/main/java/totalcross/ui/Edit.java index b64d111ca8..7efd6f8a4d 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/Edit.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/Edit.java @@ -869,6 +869,13 @@ public boolean isEditable() { return editable; } + private void invalidateImages() { + if (npback != null) { + npback.freeTexture(); + npback = null; + } + } + public Rect oldBounds; @Override protected void onBoundsChanged(boolean screenChanged) // guich @@ -892,7 +899,7 @@ protected void onBoundsChanged(boolean screenChanged) // guich } xMax = this.width - xMin; gap = hasBorder ? (xMin >> 1) : 0; - npback = null; + invalidateImages(); // material if (this.getRect().equals(oldBounds)) { @@ -936,7 +943,7 @@ public int getPreferredHeight() { @Override protected void onColorsChanged(boolean colorsChanged) { - npback = null; + invalidateImages(); fColor = getForeColor(); back0 = UIColors.sameColors ? backColor : Color.brighter(getBackColor()); // guich@572_15 back1 = back0 != Color.WHITE ? (UIColors.sameColors ? Color.darker(getBackColor()) : backColor) diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/Grid.java b/TotalCrossSDK/src/main/java/totalcross/ui/Grid.java index d32cbce16e..7739afff84 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/Grid.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/Grid.java @@ -170,7 +170,6 @@ public String[][] getItems(int startIndex, int count) return null; } *
    - * See the AddressBook sample on Litebase. * @see #lastStartingRow */ public interface DataSource // guich@570_87 diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/MainWindow.java b/TotalCrossSDK/src/main/java/totalcross/ui/MainWindow.java index 354ecd2054..e59505b8bf 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/MainWindow.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/MainWindow.java @@ -33,6 +33,7 @@ import totalcross.ui.font.Font; import totalcross.ui.gfx.Color; import totalcross.ui.gfx.Graphics; +import totalcross.ui.gfx.Rect; import totalcross.ui.image.Image; import totalcross.unit.UIRobot; import totalcross.util.Hashtable; @@ -137,6 +138,35 @@ public MainWindow(String title, byte style) // guich@112 FirebaseManager.getInstance().setMessagingService(initFirebaseMessagingService()); } + @Override + protected void getClientRect(Rect r) // guich@450_36 + { + super.getClientRect(r); + + Insets i = MainWindow.getSafeAreaInsets(); + if (this.y >= 0 && this.y < i.top) { + r.y += i.top; + } + if (this.height > Settings.screenHeight - (i.top + i.bottom)) { + r.height -= i.top + i.bottom; + } + if (this.x >= 0 && this.x < i.left) { + r.x += i.left; + } + if (this.width > Settings.screenWidth - (i.left + i.right)) { + r.width -= i.left + i.right; + } + } + + @Override + protected void fillBackground(Graphics g, int b) { + g.backColor = UIColors.unsafeAreaColor; + g.fillRect(0, 0, width, height); + + Rect r = getClientRect(); + super.fillBackground(g, b, foreColor, r.x, r.y, r.width, r.height); + } + /** * Register your own FirebaseInstanceIdService when initializing the app * @return @@ -472,9 +502,8 @@ public void onExit() { /** * Called just after the application is minimized. * - * If the user press the home key and then forces the application to stop (by going to the Settings / Applications), then - * all Litebase tables may be corrupted (actually, no data is lost, but a TableNotClosedException will be issued). So, its a good - * thing to call LitebaseConnection.closeAll in your litebase instances and recover them in the onRestore method. + * If the user press the home key and then forces the application to stop. So, its a good + * thing to close IO operations and recover them in the onRestore method. *

    * When the onMinimize is called, the screen will only be able to be updated after it resumes (in other words, * calling repaint or repaintNow from the onMinimize method has no effect). diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/MultiEdit.java b/TotalCrossSDK/src/main/java/totalcross/ui/MultiEdit.java index 2318383622..56aabce470 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/MultiEdit.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/MultiEdit.java @@ -561,7 +561,7 @@ private void calculateFirst() // guich@320_28: completely redesigned - guich@581 int n = chars.length(); int pos = 0; for (; pos < n; pos++) { - int pos0 = pos == 0 || chars.charAt(pos - 1) < ' ' ? pos : pos - 1; // guich@tc113_37: when parsing "Update of /pcvsroot/LitebaseSDK/src/native/parser", it was breaking in the first /, but in the next loop iteration, it was skipping the first /, and, thus, computing a character less + int pos0 = pos == 0 || chars.charAt(pos - 1) < ' ' ? pos : pos - 1; // guich@tc113_37: when parsing "Update of /pcvsroot/src/native/parser", it was breaking in the first /, but in the next loop iteration, it was skipping the first /, and, thus, computing a character less first.addElement(pos = Convert.getBreakPos(fm, chars, pos0, tw, true)); // guich@tc166: we'll take care of the initial space/ENTER during drawing } first.addElement(n); @@ -1335,7 +1335,14 @@ protected void draw(Graphics g) { g.drawRect(z1.x, z1.y - (spaceBetweenLines >> 1), 1, hLine); } if (Window.isScreenShifted() && lastZ1y != z1.y) { - getParentWindow().shiftScreen(this, lastZ1y = z1.y); + for (Container c = parent; c != null; c = c.parent) { + if (c instanceof ScrollContainer) { + ScrollContainer scrollContainer = (ScrollContainer) c; + scrollContainer.scrollToControl(this); + break; + } + } + lastZ1y = z1.y; } } else { cursorShowing = false; diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/NinePatch.java b/TotalCrossSDK/src/main/java/totalcross/ui/NinePatch.java index 7a8b6a0cd7..ab4a52ee17 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/NinePatch.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/NinePatch.java @@ -3,360 +3,400 @@ // // SPDX-License-Identifier: LGPL-2.1-only -package totalcross.ui; + package totalcross.ui; -import totalcross.res.Resources; -import totalcross.sys.Convert; -import totalcross.sys.Settings; -import totalcross.sys.Vm; -import totalcross.ui.gfx.Color; -import totalcross.ui.gfx.Graphics; -import totalcross.ui.image.Image; -import totalcross.ui.image.ImageException; -import totalcross.util.Hashtable; -import totalcross.util.concurrent.Lock; + import java.util.Arrays; + import java.util.HashMap; + + import totalcross.res.Resources; + import totalcross.sys.Convert; + import totalcross.sys.Settings; + import totalcross.sys.Vm; + import totalcross.ui.gfx.Color; + import totalcross.ui.gfx.Graphics; + import totalcross.ui.image.Image; + import totalcross.ui.image.ImageException; + import totalcross.util.Hashtable; + import totalcross.util.concurrent.Lock; + + /** NinePatch is a class that creates a button of any size by dividing a + * sample button into 9 parts: the 4 corners, the 4 sides, and the middle. + * + * Corner are drawn unscaled, sides are resized in a single direction, and the middle + * is resized, colorized and then dithered. + * + * This class is thread-safe. + * + * @since TotalCross 1.3 + */ + public class NinePatch { + public static final int BUTTON = 0; + public static final int EDIT = 1; + public static final int COMBOBOX = 2; + public static final int LISTBOX = 3; + public static final int MULTIEDIT = 4; + public static final int PROGRESSBARV = 5; + public static final int PROGRESSBARH = 4; + public static final int SCROLLPOSH = 6; + public static final int SCROLLPOSV = 7; + public static final int TAB = 8; + public static final int GRID = 9; + public static final int TAB2 = 10; + public static final int MULTIBUTTON = 11; + + static NinePatch instance; + + /** Defines if we will use the Image.applyColor (1) or Image.applyColor2 (2) algorithms + * when getting the pressed instance. */ + public static int pressColorAlgorithm = 1; + + public static NinePatch getInstance() { + return instance != null ? instance : (instance = new NinePatch()); + } + + private NinePatch() { + switch (Settings.uiStyle) { + case Settings.Holo: + parts = new Parts[] { load(Resources.button, 7, 4), load(Resources.edit, 16, 2), load(Resources.combobox, 9, 5), + load(Resources.listbox, 10, 5), load(Resources.multiedit, 9, 4), load(Resources.progressbarv, 9, 4), + load(Resources.scrollposh, 3, 2), load(Resources.scrollposv, 3, 2), load(Resources.tab, 10, 4), + load(Resources.grid, 5, 3), load(Resources.tab2, 18, 3), load(Resources.multibutton, 5, 2), }; + break; + case Settings.Material: + parts = new Parts[] { load(Resources.button, 7, 3), load(Resources.combobox, 9, 5), load(Resources.combobox, 9, 5), + load(Resources.listbox, 10, 5), null, load(Resources.progressbarv, 9, 4), load(Resources.scrollposh, 3, 2), + load(Resources.scrollposv, 3, 2), load(Resources.tab, 10, 4), load(Resources.grid, 5, 3), + load(Resources.tab2, 10, 2), load(Resources.multibutton, 5, 2), }; + break; + default: + parts = new Parts[] { load(Resources.button, 7, 1), load(Resources.edit, 5, 3), load(Resources.combobox, 9, 5), + load(Resources.listbox, 5, 3), load(Resources.multiedit, 9, 4), load(Resources.progressbarv, 9, 4), + load(Resources.scrollposh, 3, 2), load(Resources.scrollposv, 3, 2), load(Resources.tab, 10, 4), + load(Resources.grid, 5, 3), load(Resources.tab2, 10, 2), load(Resources.multibutton, 5, 2), }; + break; + } + } + + public class Parts { + final Image imgLT, imgT, imgRT, imgL, imgC, imgR, imgLB, imgB, imgRB; // left top right bottom + final int scalableAreaStartWidth, scalableAreaEndWidth, scalableAreaStartHeight, scalableAreaEndHeight; + + public Parts(Image original, int scalableAreaStartWidth, int scalableAreaEndWidth, int scalableAreaStartHeight, + int scalableAreaEndHeight) throws ImageException { + this.scalableAreaStartWidth = scalableAreaStartWidth; + this.scalableAreaEndWidth = scalableAreaEndWidth; + this.scalableAreaStartHeight = scalableAreaStartHeight; + this.scalableAreaEndHeight = scalableAreaEndHeight; + + int w = original.getWidth(); + int h = original.getHeight(); + int[] buf = new int[w > h ? w : h]; + + this.imgLT = getImageArea(buf, original, 0, 0, scalableAreaStartWidth, scalableAreaStartHeight); + this.imgRT = getImageArea(buf, original, scalableAreaEndWidth, 0, w - scalableAreaEndWidth, scalableAreaStartHeight); + this.imgLB = getImageArea(buf, original, 0, scalableAreaEndHeight, scalableAreaStartWidth, h - scalableAreaEndHeight); + this.imgRB = getImageArea(buf, original, scalableAreaEndWidth, scalableAreaEndHeight, w - scalableAreaEndWidth, h - scalableAreaEndHeight); + this.imgT = getImageArea(buf, original, scalableAreaStartWidth, 0, scalableAreaEndWidth - scalableAreaStartWidth, scalableAreaStartHeight); + this.imgB = getImageArea(buf, original, scalableAreaStartWidth, scalableAreaEndHeight, scalableAreaEndWidth - scalableAreaStartWidth, h - scalableAreaEndHeight); + this.imgL = getImageArea(buf, original, 0, scalableAreaStartHeight, scalableAreaStartWidth, scalableAreaEndHeight - scalableAreaStartHeight); + this.imgR = getImageArea(buf, original, scalableAreaEndWidth, scalableAreaStartHeight, w - scalableAreaEndWidth, scalableAreaEndHeight - scalableAreaStartHeight); + this.imgC = getImageArea(buf, original, scalableAreaStartWidth, scalableAreaStartHeight, scalableAreaEndWidth - scalableAreaStartWidth, scalableAreaEndHeight - scalableAreaStartHeight); + } + } + + private Lock imageLock = new Lock(); + + private Parts[] parts; + + private Hashtable htBtn = new Hashtable(100); + private Hashtable htPressBtn = new Hashtable(100); + private StringBuffer sbBtn = new StringBuffer(25); + + private void copyPixels(int[] buf, Image dst, Image src, int dstX, int dstY, int srcX, int srcY, int srcW, int srcH) { + int dstW = dst.getWidth(); + int dstH = dst.getHeight(); + if (srcW > dstW) { + srcW = dstW; + } + if (srcH > dstH) { + srcH = dstH; + } + + int y2 = srcY + srcH; + Graphics gd = dst.getGraphics(); + Graphics gs = src.getGraphics(); + + for (; srcY < y2; srcY++, dstY++) { + gs.getRGB(buf, 0, srcX, srcY, srcW, 1); + gd.setRGB(buf, 0, dstX, dstY, srcW, 1); + } + } + + private Image getImageArea(int[] buf, Image orig, int x, int y, int w, int h) throws ImageException { + Image img = new Image(w, h); + copyPixels(buf, img, orig, 0, 0, x, y, w, h); + return img; + } + + private int[] getScalableArea(Image image) { + int w = image.getWidth(); + int h = image.getHeight(); + byte[] colors = new byte[w*4]; + int scalableArea[] = new int[4]; + //LT + image.getPixelRow(colors, 0); + + int[] c2 = new int[w]; + for (int i = 0, j = 0 ; i < w ; i++, j=i*4) { + c2[i] = (colors[j+3] << 24) | (colors[j] << 16) | (colors[j+1] << 8) | colors[j+2]; + } + + int startW; + for(startW = 0 ; startW < w ; startW++) { + if (colors[(startW*4)+3] == -1) { + startW--; // Discouting the guide pixel + break; + } + } + + int endW; + for(endW = w - 1; endW >= 0 ; endW--) { + if (colors[(endW*4)+3] == -1) { + endW++; // Discouting the guide pixel + break; + } + } + + int startH; + for (startH = 0 ; startH < h ; startH++) { + image.getPixelRow(colors, startH); + if (colors[3] == -1) { + startH--; // Discouting the guide pixel + break; + } + } + + int endH; + for (endH = h - 1; endH >= 0 ; endH--) { + image.getPixelRow(colors, endH); + if (colors[3] == -1) { + endH++; // Discouting the guide pixel + break; + } + } + + + if (startW < endW && startH < endH) { + scalableArea[0] = startW; + scalableArea[1] = endW; + scalableArea[2] = startH; + scalableArea[3] = endH; + } else { + System.out.println("Failed to find guide points for this ninepatch. Printing the stack trace"); + Vm.printStackTrace(); + scalableArea[0] = 1; + scalableArea[1] = w - 2; + scalableArea[2] = 1; + scalableArea[3] = h - 2; + } + + return scalableArea; + } + + private Parts load(Image original, int scalableAreaStartWidth, int scalableAreaEndWidth, int scalableAreaStartHeight, int scalableAreaEndHeight) { + try { + Parts p = new Parts(original, scalableAreaStartWidth, scalableAreaEndWidth, scalableAreaStartHeight, scalableAreaEndHeight); + return p; + } catch (Exception e) { + throw new RuntimeException(e + " " + e.getMessage()); + } + } -/** NinePatch is a class that creates a button of any size by dividing a - * sample button into 9 parts: the 4 corners, the 4 sides, and the middle. - * - * Corner are drawn unscaled, sides are resized in a single direction, and the middle - * is resized, colorized and then dithered. - * - * This class is thread-safe. - * - * @since TotalCross 1.3 - */ -public class NinePatch { - public static final int BUTTON = 0; - public static final int EDIT = 1; - public static final int COMBOBOX = 2; - public static final int LISTBOX = 3; - public static final int MULTIEDIT = 4; - public static final int PROGRESSBARV = 5; - public static final int PROGRESSBARH = 4; - public static final int SCROLLPOSH = 6; - public static final int SCROLLPOSV = 7; - public static final int TAB = 8; - public static final int GRID = 9; - public static final int TAB2 = 10; - public static final int MULTIBUTTON = 11; + private class ScalableImage { + private Image original; + private int scalableAreaStartWidth; + private int scalableAreaStartHeight; - static NinePatch instance; + public ScalableImage(Image original, int scalableAreaStartWidth, int scalableAreaStartHeight) { + this.original = original; + this.scalableAreaStartWidth = scalableAreaStartWidth; + this.scalableAreaStartHeight = scalableAreaStartHeight; + } - /** Defines if we will use the Image.applyColor (1) or Image.applyColor2 (2) algorithms - * when getting the pressed instance. */ - public static int pressColorAlgorithm = 1; + @Override + public int hashCode() { + return Arrays.hashCode(new int[] { original.hashCode(), scalableAreaStartWidth, scalableAreaStartHeight }); + } - public static NinePatch getInstance() { - return instance != null ? instance : (instance = new NinePatch()); - } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ScalableImage)) { + return false; + } + ScalableImage other = (ScalableImage) obj; + if (this.scalableAreaStartWidth != other.scalableAreaStartWidth) { + return false; + } + if (this.scalableAreaStartHeight != other.scalableAreaStartHeight) { + return false; + } + if (this.original != null) { + return this.original.equals(other.original); + } + return other.original == null; + } + } - private NinePatch() { - switch (Settings.uiStyle) { - case Settings.Holo: - parts = new Parts[] { load(Resources.button, 7, 4), load(Resources.edit, 16, 2), load(Resources.combobox, 9, 5), - load(Resources.listbox, 10, 5), load(Resources.multiedit, 9, 4), load(Resources.progressbarv, 9, 4), - load(Resources.scrollposh, 3, 2), load(Resources.scrollposv, 3, 2), load(Resources.tab, 10, 4), - load(Resources.grid, 5, 3), load(Resources.tab2, 18, 3), load(Resources.multibutton, 5, 2), }; - break; - case Settings.Material: - parts = new Parts[] { load(Resources.button, 7, 3), load(Resources.combobox, 9, 5), load(Resources.combobox, 9, 5), - load(Resources.listbox, 10, 5), null, load(Resources.progressbarv, 9, 4), load(Resources.scrollposh, 3, 2), - load(Resources.scrollposv, 3, 2), load(Resources.tab, 10, 4), load(Resources.grid, 5, 3), - load(Resources.tab2, 10, 2), load(Resources.multibutton, 5, 2), }; - break; - default: - parts = new Parts[] { load(Resources.button, 7, 1), load(Resources.edit, 5, 3), load(Resources.combobox, 9, 5), - load(Resources.listbox, 5, 3), load(Resources.multiedit, 9, 4), load(Resources.progressbarv, 9, 4), - load(Resources.scrollposh, 3, 2), load(Resources.scrollposv, 3, 2), load(Resources.tab, 10, 4), - load(Resources.grid, 5, 3), load(Resources.tab2, 10, 2), load(Resources.multibutton, 5, 2), }; - break; - } - } + private HashMap scalableImagePartsMap = new HashMap<>(); - public class Parts { - Image imgLT, imgT, imgRT, imgL, imgC, imgR, imgLB, imgB, imgRB; // left top right bottom - int scalableAreaStartWidth, scalableAreaEndWidth, scalableAreaStartHeight, scalableAreaEndHeight; - } + public Parts load(Image original, int scalableAreaStartWidth, int scalableAreaStartHeight) { + ScalableImage scalableImage = new ScalableImage(original, scalableAreaStartWidth, scalableAreaStartHeight); + Parts p = scalableImagePartsMap.get(scalableImage); + if (p != null) { + return p; + } - private Lock imageLock = new Lock(); - - private Parts[] parts; - - private Hashtable htBtn = new Hashtable(100); - private Hashtable htPressBtn = new Hashtable(100); - private StringBuffer sbBtn = new StringBuffer(25); - - private void copyPixels(int[] buf, Image dst, Image src, int dstX, int dstY, int srcX, int srcY, int srcW, int srcH) { - int dstW = dst.getWidth(); - int dstH = dst.getHeight(); - if (srcW > dstW) { - srcW = dstW; - } - if (srcH > dstH) { - srcH = dstH; - } - - int y2 = srcY + srcH; - Graphics gd = dst.getGraphics(); - Graphics gs = src.getGraphics(); - - for (; srcY < y2; srcY++, dstY++) { - gs.getRGB(buf, 0, srcX, srcY, srcW, 1); - gd.setRGB(buf, 0, dstX, dstY, srcW, 1); - } - } - - private Image getImageArea(int[] buf, Image orig, int x, int y, int w, int h) throws ImageException { - Image img = new Image(w, h); - copyPixels(buf, img, orig, 0, 0, x, y, w, h); - return img; - } - /** - * Returns a Parts that should be used to set the npParts of a Control. - * @param original the original image with the guides. - * */ - public Parts load(Image original) { - int w = original.getWidth(); - int h = original.getHeight(); - int[] buf = new int[w > h ? w : h]; - int[] scalableAreas = getScalableArea(original); - Parts p = null; - try { - p = load(getImageArea(buf, original, 1, 1, w - 2, h - 2), scalableAreas[0], scalableAreas[1], scalableAreas[2], scalableAreas[3]); - } catch (ImageException e) { - e.printStackTrace(); - } - return p; - } - - private int[] getScalableArea(Image image) { - int w = image.getWidth(); - int h = image.getHeight(); - byte[] colors = new byte[w*4]; - int scalableArea[] = new int[4]; - //LT - image.getPixelRow(colors, 0); - - int[] c2 = new int[w]; - for (int i = 0, j = 0 ; i < w ; i++, j=i*4) { - c2[i] = (colors[j+3] << 24) | (colors[j] << 16) | (colors[j+1] << 8) | colors[j+2]; - } - - int startW; - for(startW = 0 ; startW < w ; startW++) { - if (colors[(startW*4)+3] == -1) { - startW--; // Discouting the guide pixel - break; - } - } - - int endW; - for(endW = w - 1; endW >= 0 ; endW--) { - if (colors[(endW*4)+3] == -1) { - endW++; // Discouting the guide pixel - break; - } - } - - int startH; - for (startH = 0 ; startH < h ; startH++) { - image.getPixelRow(colors, startH); - if (colors[3] == -1) { - startH--; // Discouting the guide pixel - break; - } - } - - int endH; - for (endH = h - 1; endH >= 0 ; endH--) { - image.getPixelRow(colors, endH); - if (colors[3] == -1) { - endH++; // Discouting the guide pixel - break; - } - } - - - if (startW < endW && startH < endH) { - scalableArea[0] = startW; - scalableArea[1] = endW; - scalableArea[2] = startH; - scalableArea[3] = endH; - } else { - System.out.println("Failed to find guide points for this ninepatch. Printing the stack trace"); - Vm.printStackTrace(); - scalableArea[0] = 1; - scalableArea[1] = w - 2; - scalableArea[2] = 1; - scalableArea[3] = h - 2; - } - - return scalableArea; - } - - public Parts load(Image original, int scalableAreaStartWidth, int scalableAreaEndWidth, int scalableAreaStartHeight, int scalableAreaEndHeight) { - try { - int w = original.getWidth(); - int h = original.getHeight(); - int[] buf = new int[w > h ? w : h]; - Parts p = new Parts(); - p.scalableAreaStartWidth = scalableAreaStartWidth; - p.scalableAreaEndWidth = scalableAreaEndWidth; - p.scalableAreaStartHeight = scalableAreaStartHeight; - p.scalableAreaEndHeight = scalableAreaEndHeight; - p.imgLT = getImageArea(buf, original, 0, 0, scalableAreaStartWidth, scalableAreaStartHeight); - p.imgRT = getImageArea(buf, original, scalableAreaEndWidth, 0, w - scalableAreaEndWidth, scalableAreaStartHeight); - p.imgLB = getImageArea(buf, original, 0, scalableAreaEndHeight, scalableAreaStartWidth, h - scalableAreaEndHeight); - p.imgRB = getImageArea(buf, original, scalableAreaEndWidth, scalableAreaEndHeight, w - scalableAreaEndWidth, h - scalableAreaEndHeight); - p.imgT = getImageArea(buf, original, scalableAreaStartWidth, 0, scalableAreaEndWidth - scalableAreaStartWidth, scalableAreaStartHeight); - p.imgB = getImageArea(buf, original, scalableAreaStartWidth, scalableAreaEndHeight, scalableAreaEndWidth - scalableAreaStartWidth, h - scalableAreaEndHeight); - p.imgL = getImageArea(buf, original, 0, scalableAreaStartHeight, scalableAreaStartWidth, scalableAreaEndHeight - scalableAreaStartHeight); - p.imgR = getImageArea(buf, original, scalableAreaEndWidth, scalableAreaStartHeight, w - scalableAreaEndWidth, scalableAreaEndHeight - scalableAreaStartHeight); - p.imgC = getImageArea(buf, original, scalableAreaStartWidth, scalableAreaStartHeight, scalableAreaEndWidth - scalableAreaStartWidth, scalableAreaEndHeight - scalableAreaStartHeight); - return p; - } catch (Exception e) { - throw new RuntimeException(e + " " + e.getMessage()); - } - } - -/** - * - * @param original teste0 - * @param side teste - * @param corner teste2 - * */ - public Parts load(Image original, int scalableAreaWidth, int scalableAreaHeight) { - try { - int w = original.getWidth(); - int h = original.getHeight(); - int[] buf = new int[w > h ? w : h]; - Parts p = new Parts(); - p.scalableAreaStartWidth = scalableAreaWidth; - p.scalableAreaEndWidth = original.getWidth() - scalableAreaWidth; - p.scalableAreaStartHeight = scalableAreaHeight; - p.scalableAreaEndHeight = original.getHeight() - scalableAreaHeight; - p.imgLT = getImageArea(buf, original, 0, 0, scalableAreaWidth, scalableAreaHeight); - p.imgRT = getImageArea(buf, original, w - scalableAreaWidth, 0, scalableAreaWidth, scalableAreaHeight); - p.imgLB = getImageArea(buf, original, 0, h - scalableAreaHeight, scalableAreaWidth, scalableAreaHeight); - p.imgRB = getImageArea(buf, original, w - scalableAreaWidth, h - scalableAreaHeight, scalableAreaWidth, scalableAreaHeight); - p.imgT = getImageArea(buf, original, scalableAreaWidth, 0, w - scalableAreaWidth * 2, scalableAreaHeight); - p.imgB = getImageArea(buf, original, scalableAreaWidth, h - scalableAreaHeight, w - scalableAreaWidth * 2, scalableAreaHeight); - p.imgL = getImageArea(buf, original, 0, scalableAreaHeight, scalableAreaWidth, h - scalableAreaHeight * 2); - p.imgR = getImageArea(buf, original, w - scalableAreaWidth, scalableAreaHeight, scalableAreaWidth, h - scalableAreaHeight * 2); - p.imgC = getImageArea(buf, original, scalableAreaWidth, scalableAreaHeight, w - scalableAreaWidth * 2, h - scalableAreaHeight * 2); - return p; - } catch (Exception e) { - throw new RuntimeException(e + " " + e.getMessage()); - } - } - - public Image getNormalInstance(Parts p, int width, int height, int color, boolean rotate) throws ImageException { - int[] buf = new int[width > height ? width : height]; - Image ret = new Image(width, height); - Image c; -// int side = p.side, s; -// int corner = p.corner; - int s; - int scalableAreaStartWidth = p.scalableAreaStartWidth; - int scalableAreaStartHeight = p.scalableAreaStartHeight; - // sides - s = width - (p.imgLT.getWidth() + p.imgRT.getWidth()); - if (s > 0) { - c = p.imgT.getScaledInstance(s, p.imgT.getHeight()); - copyPixels(buf, ret, c, scalableAreaStartWidth, 0, 0, 0, s, c.getHeight()); - c = p.imgB.getScaledInstance(s, p.imgB.getHeight()); - copyPixels(buf, ret, c, scalableAreaStartWidth, height - c.getHeight(), 0, 0, s, c.getHeight()); - } - s = height - (p.imgLT.getHeight() + p.imgLB.getHeight()); - if (s > 0) { - c = p.imgL.getScaledInstance(p.imgL.getWidth(), s); - copyPixels(buf, ret, c, 0, scalableAreaStartHeight, 0, 0, c.getWidth(), s); - c = p.imgR.getScaledInstance(p.imgR.getWidth(), s); - copyPixels(buf, ret, c, width - c.getWidth(), scalableAreaStartHeight, 0, 0, c.getWidth(), s); - } - // corners - try { - copyPixels(buf, ret, p.imgLT, 0, 0, 0, 0, p.imgLT.getWidth(), p.imgLT.getHeight()); - copyPixels(buf, ret, p.imgRT, width - p.imgRT.getWidth(), 0, 0, 0, p.imgRT.getWidth(), p.imgRT.getHeight()); - copyPixels(buf, ret, p.imgLB, 0, height - p.imgLB.getHeight(), 0, 0, p.imgLB.getWidth(), p.imgLB.getHeight()); - copyPixels(buf, ret, p.imgRB, width - p.imgRB.getWidth(), height - p.imgRB.getHeight(), 0, 0, p.imgRB.getWidth(), p.imgRB.getHeight()); - } catch (ArrayIndexOutOfBoundsException aioobe) { - } // ignore error that comes sometimes in JavaSE - // center - int cW = width - (p.imgLT.getWidth() + p.imgRT.getWidth()); - int cH = height - (p.imgLT.getHeight() + p.imgLB.getHeight()); - if (cW > 0 && cH > 0) { - c = p.imgC.getScaledInstance(cW, cH); // smoothscale generates a worst result because it enhances the edges - copyPixels(buf, ret, c, scalableAreaStartWidth, scalableAreaStartHeight, 0, 0, cW, cH); - } - if (Settings.screenBPP == 16) { - ret.getGraphics().dither(0, 0, ret.getWidth(), ret.getHeight()); - } - if (color != -1) { - ret.applyColor2(color); - } - if (rotate) { - ret = ret.getRotatedScaledInstance(100, 180, -1); - } - return ret; - } - - // rotate is only used by TabbedContainer - public Image getNormalInstance(int type, int width, int height, int color, boolean rotate) throws ImageException { - Image ret = null; - synchronized (imageLock) { - int hash = 0; - sbBtn.setLength(0); - hash = Convert.hashCode(sbBtn.append(type).append('|').append(width).append('|').append(height).append('|') - .append(color).append('|').append(rotate)); - ret = (Image) htBtn.get(hash); - if (ret == null) { - ret = getNormalInstance(parts[type], width, height, color, rotate); - htBtn.put(hash, ret); - } - } - return ret; - } - - public Image getPressedInstance(Image img, int backColor, int pressColor) throws ImageException { - Image pressed = null; - sbBtn.setLength(0); - int hash = Convert.hashCode(sbBtn.append(img).append('|').append(backColor).append('|').append(pressColor)); - pressed = (Image) htPressBtn.get(hash); - if (pressed == null) { - synchronized (imageLock) { - if (pressColor != -1) { - pressed = img.getFrameInstance(0); // get a copy of the image - if (pressColorAlgorithm == 1) { - pressed.applyColor(pressColor); // colorize it - } else { - pressed.applyColor2(pressColor); // colorize it - } - } else { - pressed = img.getTouchedUpInstance(Color.getBrightness(backColor) > (256 - 32) ? (byte) -64 : (byte) 32, - (byte) 0); - } - htPressBtn.put(hash, pressed); - } - } - return pressed; - } - - public void flush() { - htBtn.clear(); - htPressBtn.clear(); - } - - /** Used internally to prevent Out of Memory errors. */ - public static void tryDrawImage(Graphics g, Image npback, int x, int y) { - try { - if (npback != null) { - g.drawImage(npback, x, y); - } - } catch (OutOfMemoryError oome) { - getInstance().flush(); // release memory and try again - g.drawImage(npback, x, y); - } - } -} + int scalableAreaEndWidth = original.getWidth() - scalableAreaStartWidth; + int scalableAreaEndHeight = original.getHeight() - scalableAreaStartHeight; + p = load(original, scalableAreaStartWidth, scalableAreaEndWidth, scalableAreaStartHeight, scalableAreaEndHeight); + scalableImagePartsMap.put(scalableImage, p); + return p; + } + + private HashMap simpleLoadMap = new HashMap<>(); + + /** + * Returns a Parts that should be used to set the npParts of a Control. + * @param original the original image with the guides. + * */ + public Parts load(Image original) { + Parts p = simpleLoadMap.get(original); + if (p != null) { + return p; + } + + int w = original.getWidth(); + int h = original.getHeight(); + int[] buf = new int[w > h ? w : h]; + int[] scalableAreas = getScalableArea(original); + try { + p = load(getImageArea(buf, original, 1, 1, w - 2, h - 2), scalableAreas[0], scalableAreas[1], scalableAreas[2], scalableAreas[3]); + simpleLoadMap.put(original, p); + } catch (ImageException e) { + e.printStackTrace(); + } + return p; + } + + public Image getNormalInstance(Parts p, int width, int height, int color, boolean rotate) throws ImageException { + int[] buf = new int[width > height ? width : height]; + Image ret = new Image(width, height); + Image c; + // int side = p.side, s; + // int corner = p.corner; + int s; + int scalableAreaStartWidth = p.scalableAreaStartWidth; + int scalableAreaStartHeight = p.scalableAreaStartHeight; + // sides + s = width - (p.imgLT.getWidth() + p.imgRT.getWidth()); + if (s > 0) { + c = p.imgT.getScaledInstance(s, p.imgT.getHeight()); + copyPixels(buf, ret, c, scalableAreaStartWidth, 0, 0, 0, s, c.getHeight()); + c = p.imgB.getScaledInstance(s, p.imgB.getHeight()); + copyPixels(buf, ret, c, scalableAreaStartWidth, height - c.getHeight(), 0, 0, s, c.getHeight()); + } + s = height - (p.imgLT.getHeight() + p.imgLB.getHeight()); + if (s > 0) { + c = p.imgL.getScaledInstance(p.imgL.getWidth(), s); + copyPixels(buf, ret, c, 0, scalableAreaStartHeight, 0, 0, c.getWidth(), s); + c = p.imgR.getScaledInstance(p.imgR.getWidth(), s); + copyPixels(buf, ret, c, width - c.getWidth(), scalableAreaStartHeight, 0, 0, c.getWidth(), s); + } + // corners + try { + copyPixels(buf, ret, p.imgLT, 0, 0, 0, 0, p.imgLT.getWidth(), p.imgLT.getHeight()); + copyPixels(buf, ret, p.imgRT, width - p.imgRT.getWidth(), 0, 0, 0, p.imgRT.getWidth(), p.imgRT.getHeight()); + copyPixels(buf, ret, p.imgLB, 0, height - p.imgLB.getHeight(), 0, 0, p.imgLB.getWidth(), p.imgLB.getHeight()); + copyPixels(buf, ret, p.imgRB, width - p.imgRB.getWidth(), height - p.imgRB.getHeight(), 0, 0, p.imgRB.getWidth(), p.imgRB.getHeight()); + } catch (ArrayIndexOutOfBoundsException aioobe) { + } // ignore error that comes sometimes in JavaSE + // center + int cW = width - (p.imgLT.getWidth() + p.imgRT.getWidth()); + int cH = height - (p.imgLT.getHeight() + p.imgLB.getHeight()); + if (cW > 0 && cH > 0) { + c = p.imgC.getScaledInstance(cW, cH); // smoothscale generates a worst result because it enhances the edges + copyPixels(buf, ret, c, scalableAreaStartWidth, scalableAreaStartHeight, 0, 0, cW, cH); + } + if (Settings.screenBPP == 16) { + ret.getGraphics().dither(0, 0, ret.getWidth(), ret.getHeight()); + } + if (color != -1) { + ret.applyColor2(color); + } + if (rotate) { + ret = ret.getRotatedScaledInstance(100, 180, -1); + } + return ret; + } + + // rotate is only used by TabbedContainer + public Image getNormalInstance(int type, int width, int height, int color, boolean rotate) throws ImageException { + Image ret = null; + synchronized (imageLock) { + int hash = 0; + sbBtn.setLength(0); + hash = Convert.hashCode(sbBtn.append(type).append('|').append(width).append('|').append(height).append('|') + .append(color).append('|').append(rotate)); + ret = (Image) htBtn.get(hash); + if (ret == null) { + ret = getNormalInstance(parts[type], width, height, color, rotate); + htBtn.put(hash, ret); + } + } + return ret; + } + + public Image getPressedInstance(Image img, int backColor, int pressColor) throws ImageException { + Image pressed = null; + sbBtn.setLength(0); + int hash = Convert.hashCode(sbBtn.append(img).append('|').append(backColor).append('|').append(pressColor)); + pressed = (Image) htPressBtn.get(hash); + if (pressed == null) { + synchronized (imageLock) { + if (pressColor != -1) { + pressed = img.getFrameInstance(0); // get a copy of the image + if (pressColorAlgorithm == 1) { + pressed.applyColor(pressColor); // colorize it + } else { + pressed.applyColor2(pressColor); // colorize it + } + } else { + pressed = img.getTouchedUpInstance(Color.getBrightness(backColor) > (256 - 32) ? (byte) -64 : (byte) 32, + (byte) 0); + } + htPressBtn.put(hash, pressed); + } + } + return pressed; + } + + public void flush() { + htBtn.clear(); + htPressBtn.clear(); + simpleLoadMap.clear(); + scalableImagePartsMap.clear(); + } + + /** Used internally to prevent Out of Memory errors. */ + public static void tryDrawImage(Graphics g, Image npback, int x, int y) { + try { + if (npback != null) { + g.drawImage(npback, x, y); + } + } catch (OutOfMemoryError oome) { + getInstance().flush(); // release memory and try again + g.drawImage(npback, x, y); + } + } + } + \ No newline at end of file diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/ScrollContainer.java b/TotalCrossSDK/src/main/java/totalcross/ui/ScrollContainer.java index 3f0121e73f..0361ce5fb7 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/ScrollContainer.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/ScrollContainer.java @@ -227,7 +227,7 @@ public boolean scrollContent(int dx, int dy, boolean fromFlick) { isFromFlick = fromFlick; if (isFromFlick) { // In case of flick, scroll instantaneously - willScroll = internalScrollContent(dx, dy, fromFlick); + willScroll = internalScrollContent(dx, dy, fromFlick); resetTarget(); } else { // In case of a drag, compute the target position and move to it smoothly @@ -274,7 +274,10 @@ public boolean scrollContent(int dx, int dy, boolean fromFlick) { } } - if(startScroll && willScroll) postEvent(new ScrollEvent(ScrollEvent.SCROLL_START)); + if ((startScroll || willScroll) && !scrollStarted) { + scrollStarted = true; + postEvent(new ScrollEvent(ScrollEvent.SCROLL_START)); + } return willScroll; } @@ -311,11 +314,16 @@ public void updateListenerTriggered(int elapsedMilliseconds) { internalScrollContent((int)dx, (int)dy, isFromFlick); } } + + boolean scrollStarted = false; private boolean internalScrollContent(int dx, int dy, boolean fromFlick) { boolean scrolled = false; if((sbV != null || sbH != null) && dx == 0 && dy == 0) { - postEvent(new ScrollEvent(ScrollEvent.SCROLL_END)); + if (scrollStarted) { + scrollStarted = false; + postEvent(new ScrollEvent(ScrollEvent.SCROLL_END)); + } } if (dx != 0 && sbH != null) { int oldValue = sbH.value; diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/ScrollPosition.java b/TotalCrossSDK/src/main/java/totalcross/ui/ScrollPosition.java index 0e9cd0a05e..4bd41be9c7 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/ScrollPosition.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/ScrollPosition.java @@ -44,7 +44,8 @@ public class ScrollPosition extends ScrollBar implements Scrollable, PenListener private boolean neverShow; private boolean showHandle = true; - private NinePatch.Parts types[]; + private static NinePatch.Parts npPartsVertical; + private static NinePatch.Parts npPartsHorizontal; /** Set to false to make the PositionBar always show (instead of the default auto-hide behaviour). */ public static boolean AUTO_HIDE = true; @@ -90,10 +91,13 @@ public ScrollPosition(byte orientation) { disableBlockIncrement = true; enableAutoScroll = false; tempShow(); - - types = new NinePatch.Parts[2]; - types[0] = NinePatch.getInstance().load(Resources.scrollposv, 3, 2); - types[1] = NinePatch.getInstance().load(Resources.scrollposh, 3, 2); + + if (orientation == VERTICAL && npPartsVertical == null) { + npPartsVertical = NinePatch.getInstance().load(Resources.scrollposv, 3, 2); + } + if (orientation == HORIZONTAL && npPartsHorizontal == null) { + npPartsHorizontal = NinePatch.getInstance().load(Resources.scrollposh, 3, 2); + } } /** Call this to never show the ScrollPosition */ @@ -196,8 +200,9 @@ public void onPaint(Graphics g) { return; */ } if (npback == null || ((verticalBar ? npback.getHeight() : npback.getWidth()) != dragBarSize)) { - if(npParts == null) - setNinePatch(verticalBar ? Resources.scrollposv : Resources.scrollposh, 3, 2); + if (npParts == null) { + npParts = verticalBar ? npPartsVertical : npPartsHorizontal; + } try { if (verticalBar) { npback = NinePatch.getInstance().getNormalInstance(npParts, width, dragBarSize, barColor, diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/UIColors.java b/TotalCrossSDK/src/main/java/totalcross/ui/UIColors.java index 3cc21b1b20..5c5e04944a 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/UIColors.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/UIColors.java @@ -155,11 +155,20 @@ private UIColors() { /** NumericBox action color. */ public static int numericboxAction = keyboardAction; - /** The color that will be used to paint the disabled area when a screen shift occurs. + /** + * Use {@link #unsafeAreaColor} instead. + * * @since TotalCross 1.3 */ + @Deprecated public static int shiftScreenColor = 0x808080; + /** + * Color used to fill the unsafe areas of the screen and the disabled area when + * the screen is shifted. Default color is black. + */ + public static int unsafeAreaColor = Color.BLACK; + /** Background color of the clipboard menu that is opened at Edit and MultiEdit. */ public static int clipboardBack = Color.YELLOW; /** Foreground color of the clipboard menu that is opened at Edit and MultiEdit. */ diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/Window.java b/TotalCrossSDK/src/main/java/totalcross/ui/Window.java index 7b4a92b543..759e73df47 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/Window.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/Window.java @@ -30,6 +30,7 @@ import totalcross.ui.gfx.Coord; import totalcross.ui.gfx.Graphics; import totalcross.ui.gfx.Rect; +import totalcross.ui.image.ImageException; import totalcross.ui.media.Sound; import totalcross.unit.UIRobot; import totalcross.util.ElementNotFoundException; @@ -294,6 +295,8 @@ public class Window extends Container { /** A key listener that have priority over all other listeners. */ public static KeyListener keyHook; + private static Insets safeAreaInsets = new Insets(); + //////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// /** Constructs a window with no title and no border. */ @@ -654,7 +657,7 @@ final public void _postEvent(int type, int key, int x, int y, int modifiers, int return; } if (Settings.isWindowsDevice() && shiftY > 0) { - shiftY = 0; // required for WP8. not sure on other platforms + shiftY = 0; // was required for WP8. not sure on other platforms } if (shiftY != 0) // is the screen shifted? { @@ -1157,6 +1160,12 @@ private int getDirection(int originX, int originY, int x, int y) // guich@tc122_ } } + @ReplacedByNativeOnDeploy + public static Insets getSafeAreaInsets() { + Insets preset = Settings.isLandscape() ? Launcher.instance.toInsetsLandscape : Launcher.instance.toInsetsPortrait; + return preset != null ? preset : safeAreaInsets; + } + //////////////////////////////////////////////////////////////////////////////////// /** Returns the client rect, ie, the rect minus the border and title area, in relative coords * In this version, you provide the created Rect to be filled with the coords. @@ -1164,8 +1173,8 @@ private int getDirection(int originX, int originY, int x, int y) // guich@tc122_ @Override protected void getClientRect(Rect r) // guich@450_36 { - int m = borderGaps[borderStyle]; - boolean onlyBorder = borderStyle == NO_BORDER + int m = (borderStyle < NO_BORDER || borderStyle > VERTICAL_GRADIENT) ? 0 : borderGaps[borderStyle]; + boolean onlyBorder = borderStyle <= NO_BORDER || ((title == null || title.isEmpty()) && (borderStyle == ROUND_BORDER && uiAndroid)); r.x = m; @@ -1181,7 +1190,7 @@ protected void getClientRect(Rect r) // guich@450_36 r.width = this.width - m - m; r.height = this.height - r.y - m; } - + //////////////////////////////////////////////////////////////////////////////////// /** Returns the client rect, ie, the rect minus the border and title area, in relative coords */ diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/CalculatorBox.java b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/CalculatorBox.java index a7f824409c..1ab26e02eb 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/CalculatorBox.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/CalculatorBox.java @@ -125,7 +125,7 @@ private void setupUI(boolean isReposition) // guich@tc100b5_28 { setBackColor(showOperations ? UIColors.calculatorBack : UIColors.numericboxBack); // before control definitions! - setRect(LEFT, TOP, WILL_RESIZE, WILL_RESIZE); + setRect(CENTER, CENTER, WILL_RESIZE, WILL_RESIZE); int hh = fmH * 2; @@ -217,7 +217,6 @@ private void setupUI(boolean isReposition) // guich@tc100b5_28 setInsets(2, 2, 2, 2); resize(); - setRect(CENTER, CENTER, KEEP, KEEP); numericPad.setBackColor(showOperations ? UIColors.calculatorBack : UIColors.numericboxBack); pbgAction.setBackColor(showOperations ? UIColors.calculatorAction : UIColors.numericboxAction); diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/ColorChooserBox.java b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/ColorChooserBox.java index 88235b6120..dfb77a77bb 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/ColorChooserBox.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/ColorChooserBox.java @@ -69,7 +69,7 @@ public ColorChooserBox(int defaultColor) { uiAdjustmentsBasedOnFontHeightIsSupported = false; setBackColor(UIColors.colorchooserboxBack); setForeColor(UIColors.colorchooserboxFore); - setRect(LEFT, TOP, FILL, FILL); + setRect(CENTER, CENTER, SCREENSIZEMIN + 85, SCREENSIZEMIN + 85); } @Override diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FileChooserBox.java b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FileChooserBox.java index 06fc2f3ee2..495d8470a3 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FileChooserBox.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FileChooserBox.java @@ -207,7 +207,7 @@ protected void onPopup() // guich@tc100b5_28 } return; } - setRect(LEFT + 5, TOP + 5, FILL - 5, FILL - 5); + setRect(CENTER, CENTER, SCREENSIZE + 80, SCREENSIZE + 80); setBackForeColors(UIColors.fileChooserBack, UIColors.fileChooserFore); if (cbRoot != null) // guich@tc126_10 diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FilePicker.java b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FilePicker.java new file mode 100644 index 0000000000..73761d2725 --- /dev/null +++ b/TotalCrossSDK/src/main/java/totalcross/ui/dialog/FilePicker.java @@ -0,0 +1,47 @@ +/********************************************************************************* + * TotalCross Software Development Kit * + * Copyright (C) 2000-2012 SuperWaba Ltda. * + * All Rights Reserved * + * * + * This library and virtual machine is distributed in the hope that it will * + * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * * + * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * + * A copy of this license is located in file license.txt at the root of this * + * SDK or can be downloaded here: * + * http://www.gnu.org/licenses/lgpl-3.0.txt * + * * + *********************************************************************************/ + +package totalcross.ui.dialog; + +import totalcross.io.File; +import totalcross.io.IOException; +import totalcross.net.URI; +import totalcross.sys.Settings; + +public class FilePicker { + + public URI present() { + if (Settings.isIOS() || Settings.ANDROID.equals(Settings.platform)) { + return nativePresent(); + } + FileChooserBox fcb = new FileChooserBox(new FileChooserBox.Filter() { + + @Override + public boolean accept(File f) throws IOException { + return true; + } + + }); + fcb.popup(); + return new URI("file://" + fcb.getAnswer()); + } + + public FilePicker() { + + } + + native private URI nativePresent(); +} diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/gfx/Graphics.java b/TotalCrossSDK/src/main/java/totalcross/ui/gfx/Graphics.java index 0bea545c61..36774d1bdd 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/gfx/Graphics.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/gfx/Graphics.java @@ -169,6 +169,7 @@ public Graphics(GfxSurface surface) { @ReplacedByNativeOnDeploy private void create(GfxSurface surface) { + alpha = 0xFF000000; // On Java, alpha is always initialized with opaque if (surface instanceof Image) { pitch = ((Image) surface).getWidth(); } else if (surface instanceof Control) { diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/image/Image.java b/TotalCrossSDK/src/main/java/totalcross/ui/image/Image.java index f31833c25f..468679d938 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/image/Image.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/image/Image.java @@ -10,6 +10,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.imageio.ImageIO; @@ -443,6 +444,21 @@ public Graphics getGraphics() { public void applyChanges() { } + /** + * In OpenGL platforms, attempts to free the texture associated with this + * object, if any. Does nothing if no texture is associated with this object, or + * if the texture associated is shared among multiple objects. + * This method provides a way of releasing memory resources without having to + * wait for garbage collection, but it can impact negatively on performance if + * used on objects that are still in use, causing the textures to be recreated. + * + * In non-OpenGL, does nothing. + * + * AVOID USING THIS METHOD IF UNSURE ABOUT IT. + */ + public void freeTexture() { + } + /** In OpenGL platforms, apply changes to the current texture and * frees the memory used for the pixels in internal memory (the * image can, however, be drawn on screen because the texture will @@ -2386,4 +2402,25 @@ public static Image getJpegScaled(String path, int scaleNumerator, int scaleDeno final double scale = scaleNumerator / scaleDenominator; return new Image(path).smoothScaledBy(scale, scale); } + + @Override + public int hashCode() { + int[] p = (int[]) (frameCount == 1 ? this.pixels : this.pixelsOfAllFrames); + if (p != null) { + try { + /* + * If bigger than 64x64, scale down for faster hashing. + * getScaledInstance is faster because it has native implementation. + */ + if (p.length > 4096) { + Image i = this.getScaledInstance(64, 64); + return Arrays.hashCode(i.pixels); + } + } catch (ImageException e) { + e.printStackTrace(); + } + return Arrays.hashCode(p); + } + return Arrays.hashCode(p); + } } diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/image/Image4D.java b/TotalCrossSDK/src/main/java/totalcross/ui/image/Image4D.java index 5f8d6b1f3f..f775cc8df9 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/image/Image4D.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/image/Image4D.java @@ -6,6 +6,8 @@ package totalcross.ui.image; +import java.util.Arrays; + import com.totalcross.annotations.ReplacedByNativeOnDeploy; import totalcross.io.ByteArrayStream; @@ -75,6 +77,9 @@ public class Image4D extends GfxSurface { public int lastAccess = -1; int textureId = -1; + /** Used by lockChanges to store the hashCode before discarding the pixels */ + private int hashCode = 0; + // object private int[] pixels; // must be at Object position 0 protected int[] pixelsOfAllFrames; @@ -587,7 +592,7 @@ private Image4D getCopy(int w, int h) throws ImageException { native public Image setTransparentColor(int color); - native private void freeTexture(); + native public void freeTexture(); @Override public void finalize() { @@ -600,6 +605,12 @@ public void lockChanges() { if (changed[0]) { applyChanges(); } + // calculate hashCode before discarding the pixels + int[] p = (int[]) (frameCount == 1 ? this.pixels : this.pixelsOfAllFrames); + if (p != null) { + hashCode = 0; // must be reset before calling hashCode() + hashCode = this.hashCode(); + } pixels = pixelsOfAllFrames = null; } } @@ -979,6 +990,33 @@ public static void resizeJpeg(String inputPath, String outputPath, int maxPixelS } } + @Override + public int hashCode() { + // 1. lockChanges has already set hashCode + if (hashCode != 0) { + return hashCode; + } + // 2. image hasn't been locked, calculate from pixels + int[] p = (int[]) (frameCount == 1 ? this.pixels : this.pixelsOfAllFrames); + if (p != null) { + try { + /* + * If bigger than 64x64, scale down for faster hashing. + * getScaledInstance is faster because it has native implementation. + */ + if (p.length > 4096) { + Image4D i = this.getScaledInstance(64, 64); + return Arrays.hashCode(i.pixels); + } + } catch (ImageException e) { + e.printStackTrace(); + } + return Arrays.hashCode(p); + } + // 3. hashCode not set and no pixels to calculate from? let Object handle it + return super.hashCode(); + } + private static boolean hasNativeResizeJpeg() { return Settings.isIOS(); } diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/image/ImageLoader.java b/TotalCrossSDK/src/main/java/totalcross/ui/image/ImageLoader.java index cf22291e27..a807f20210 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/image/ImageLoader.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/image/ImageLoader.java @@ -8,10 +8,10 @@ import totalcross.ui.ImageControl; import totalcross.ui.event.SizeChangeEvent; import totalcross.ui.event.SizeChangeHandler; +import totalcross.sys.Settings; import totalcross.util.concurrent.Lock; public class ImageLoader { - public static interface ScaleType { public static final int CENTER = 0; public static final int CENTER_CLIP = 1; @@ -34,7 +34,22 @@ public static interface ScaleType { private static HashMap cache = new HashMap<>(); private static Lock lock = new Lock(); - + + /** + * Default value for jpeg memory savings for instances of ImageLoader. + */ + final public static double JPEG_MEMORY_SAVING_DEFAULT = 0.8; + + /** + * Set with a value between 0 and 1 to adjust the factor by which jpeg images + * are reduced during load to save memory when using GPU accelerated graphics + * ({@link Settings#isOpenGL} is true, otherwise this setting is ignored). + * + * Values above 1 or below 0 are ignored, and 1 is used instead. + * Default value is {@link #JPEG_MEMORY_SAVING_DEFAULT} + */ + public double jpegMemorySaving = JPEG_MEMORY_SAVING_DEFAULT; + public static ImageLoader get(String path) throws IOException { synchronized (lock) { ImageLoader loader = cache.get(path); @@ -59,19 +74,31 @@ public Image getImage(int desiredWidth, int desiredHeight) throws IOException, I } try { - i = Image.getJpegBestFit(path, desiredWidth, desiredHeight); + // If we're using GPU, accept a 20% smaller image to try saving some memory. + if (!Settings.isOpenGL || jpegMemorySaving >= 1 || jpegMemorySaving <= 0) { + i = Image.getJpegBestFit(path, desiredWidth, desiredHeight); + } else { + i = Image.getJpegBestFit(path, + (int) (desiredWidth * jpegMemorySaving), + (int) (desiredHeight * jpegMemorySaving)); + } } catch (Exception e) { - // probably not a jpeg, we'll just ignore the exception and try again as a regular image. + // probably not a jpeg, just ignore the exception and retry as a regular image. i = new Image(path); } int actualWidth = i.getWidth(); int actualHeight = i.getHeight(); if (actualWidth != desiredWidth || actualHeight != desiredHeight) { - i = - i.smoothScaledFixedAspectRatio( - desiredWidth < desiredHeight ? desiredWidth : desiredHeight, - desiredWidth > desiredHeight); + if (Settings.isOpenGL) { + i.setHwScaleFixedAspectRatio( + desiredWidth < desiredHeight ? desiredWidth : desiredHeight, + desiredWidth > desiredHeight); + } else { + i = i.smoothScaledFixedAspectRatio( + desiredWidth < desiredHeight ? desiredWidth : desiredHeight, + desiredWidth > desiredHeight); + } } // disabled mimap for now // mipmap.put(key, i); diff --git a/TotalCrossSDK/src/main/java/totalcross/ui/media/Camera.java b/TotalCrossSDK/src/main/java/totalcross/ui/media/Camera.java index 79e032f7b1..c0b3f25fac 100644 --- a/TotalCrossSDK/src/main/java/totalcross/ui/media/Camera.java +++ b/TotalCrossSDK/src/main/java/totalcross/ui/media/Camera.java @@ -108,7 +108,34 @@ public class Camera { /** The camera type; defaults to CAMERA_CUSTOM. */ public int cameraType; - /** The original camera used in TotalCross */ + /** + * Expected FPS for the output file created with CAMERA_VIDEO_RECORDER. + */ + public int targetFps = 30; + + /** + * Video bitrate in kbps. Adjust this field to increase or decrease the quality + * and the size of the output video. + *

    + * Quality is subjective and context related, therefore it's hard to define + * objective values. Higher resolutions also requires higher bitrates for better + * results. + *

    + * Values in the range of 100kbps (100_000) and 400kbps (400_000) should be + * enough for most videos up to HD resolution (1280x720 or + * {@link #CAMERACAPTURE_STILLQUALITY_NORMAL}). + *

    + * Values in the range of 1mbps (1_000_000) and 3mpbs (3_000_000) will provide + * the best quality a huge output file. + */ + public int bitrate = 200_000; + + /** + * The original camera used in TotalCross + * + * @deprecated Use {@link #CAMERA_PICTURE}, {@link #CAMERA_VIDEO_RECORDER} or {@link #CAMERA_FULL} instead. + */ + @Deprecated public static final int CAMERA_CUSTOM = 0; /** The native camera application; a copy of the image is returned. */ public static final int CAMERA_NATIVE = 1; @@ -116,6 +143,14 @@ public class Camera { public static final int CAMERA_NATIVE_NOCOPY = 2; /** Take the picture from the gallery. */ public static final int CAMERA_FROM_GALLERY = 3; + /** Start video player for the file from defaultFileName. */ + public static final int CAMERA_VIDEO_PLAYER = 4; + /** Record video. Use the fields videoTimeLimit, targetFps and stillQuality to better control the result. */ + public static final int CAMERA_VIDEO_RECORDER = 5; + /** Take picture */ + public static final int CAMERA_PICTURE = 6; + /** Combination of {@link #CAMERA_PICTURE} and {@link #CAMERA_VIDEO_RECORDER}, let the user take a picture or record a video. */ + public static final int CAMERA_FULL = 7; /** Used in the cameraMode member. */ public static final int CAMERACAPTURE_MODE_STILL = 0; @@ -271,4 +306,34 @@ private static String[] sortResolutions(String[] res) { private static String getNativeResolutions() { return null; } + + /** + * Returns the resolutions supported by the device for video recording using the + * back camera. Currently, it works only on Android, other platforms return the + * same results as {@link #getSupportedResolutions}. + * + *

    + * When the information is not available, a default list is returned and the + * first element is + * set to "Default values:". + * + * @since TotalCross 4.9.12 + */ + public static String[] getVideoResolutions() { + String[] ret = null; + if (Settings.platform.equals(Settings.ANDROID)) { + String s = getNativeVideoResolutions(); + if (s != null) { + ret = sortResolutions(Convert.tokenizeString(s, ',')); + } + } else { + ret = getSupportedResolutions(); + } + return ret; + } + + @ReplacedByNativeOnDeploy + private static String getNativeVideoResolutions() { + return null; + } } diff --git a/TotalCrossSDK/src/main/java/totalcross/unit/TestCase.java b/TotalCrossSDK/src/main/java/totalcross/unit/TestCase.java index a954685786..ff03122647 100644 --- a/TotalCrossSDK/src/main/java/totalcross/unit/TestCase.java +++ b/TotalCrossSDK/src/main/java/totalcross/unit/TestCase.java @@ -11,7 +11,6 @@ /** This class represents a testcase. Just extend this class and implement the testRun method, * calling then the assert methods. The testcase must be added to a TestSuite class using the - * addTestCase method. See the Litebase/alltests sample. */ public abstract class TestCase { diff --git a/TotalCrossSDK/src/main/java/totalcross/unit/TestSuite.java b/TotalCrossSDK/src/main/java/totalcross/unit/TestSuite.java index 24485fa330..fc8f83e7b1 100644 --- a/TotalCrossSDK/src/main/java/totalcross/unit/TestSuite.java +++ b/TotalCrossSDK/src/main/java/totalcross/unit/TestSuite.java @@ -30,7 +30,6 @@ /** JUnit implementation for TotalCross, to be used in the device (or in the desktop). * It simulates the same output that you get when using JUnit under Eclipse. * To use it, you must extend this class and add the test cases using the addTestCase method. - * See an example in the Litebase SDK, in samples/sys/testcases folder. *

    * You can also create a test suite without this user interface. Here's a sample: *

    @@ -288,7 +287,7 @@ private static String getLineNumber(Throwable e) {
           if (Settings.onJavaSE && l.startsWith(" at ")) {
             l = l.substring(4);
           }
    -      if (!l.startsWith("totalcross.") && !l.startsWith("litebase.") && (l.indexOf(' ') >= 0 || l.indexOf(':') >= 0)) {
    +      if (!l.startsWith("totalcross.") && (l.indexOf(' ') >= 0 || l.indexOf(':') >= 0)) {
             return l;
           }
         }
    diff --git a/TotalCrossSDK/src/main/java/totalcross/util/I18N.java b/TotalCrossSDK/src/main/java/totalcross/util/I18N.java
    index bdc5ec2bcd..355ee85e97 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/util/I18N.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/util/I18N.java
    @@ -3,6 +3,7 @@
     import java.io.UnsupportedEncodingException;
     import java.util.HashMap;
     import java.util.Locale;
    +import java.util.Map;
     
     import totalcross.sys.AbstractCharacterConverter;
     import totalcross.sys.Convert;
    @@ -39,202 +40,17 @@
      *
      */
     public class I18N {
    +
    +	private static final Map codes = new HashMap<>();
     	
    -	public static I18N aa;
    -	public static I18N ab;
    -	public static I18N ae;
    -	public static I18N af;
    -	public static I18N ak;
    -	public static I18N am;
    -	public static I18N an;
    -	public static I18N ar;
    -	public static I18N as;
    -	public static I18N av;
    -	public static I18N ay;
    -	public static I18N az;
    -	public static I18N ba;
    -	public static I18N be;
    -	public static I18N bg;
    -	public static I18N bh;
    -	public static I18N bi;
    -	public static I18N bm;
    -	public static I18N bn;
    -	public static I18N bo;
    -	public static I18N br;
    -	public static I18N bs;
    -	public static I18N ca;
    -	public static I18N ce;
    -	public static I18N ch;
    -	public static I18N co;
    -	public static I18N cr;
    -	public static I18N cs;
    -	public static I18N cu;
    -	public static I18N cv;
    -	public static I18N cy;
    -	public static I18N da;
    -	public static I18N de;
    -	public static I18N dv;
    -	public static I18N dz;
    -	public static I18N ee;
    -	public static I18N el;
    -	public static I18N en;
    -	public static I18N eo;
    -	public static I18N es;
    -	public static I18N et;
    -	public static I18N eu;
    -	public static I18N fa;
    -	public static I18N ff;
    -	public static I18N fi;
    -	public static I18N fj;
    -	public static I18N fo;
    -	public static I18N fr;
    -	public static I18N fy;
    -	public static I18N ga;
    -	public static I18N gd;
    -	public static I18N gl;
    -	public static I18N gn;
    -	public static I18N gu;
    -	public static I18N gv;
    -	public static I18N ha;
    -	public static I18N he;
    -	public static I18N hi;
    -	public static I18N ho;
    -	public static I18N hr;
    -	public static I18N ht;
    -	public static I18N hu;
    -	public static I18N hy;
    -	public static I18N hz;
    -	public static I18N ia;
    -	public static I18N id;
    -	public static I18N ie;
    -	public static I18N ig;
    -	public static I18N ii;
    -	public static I18N ik;
    -	public static I18N in;
    -	public static I18N io;
    -	public static I18N is;
    -	public static I18N it;
    -	public static I18N iu;
    -	public static I18N iw;
    -	public static I18N ja;
    -	public static I18N ji;
    -	public static I18N jv;
    -	public static I18N ka;
    -	public static I18N kg;
    -	public static I18N ki;
    -	public static I18N kj;
    -	public static I18N kk;
    -	public static I18N kl;
    -	public static I18N km;
    -	public static I18N kn;
    -	public static I18N ko;
    -	public static I18N kr;
    -	public static I18N ks;
    -	public static I18N ku;
    -	public static I18N kv;
    -	public static I18N kw;
    -	public static I18N ky;
    -	public static I18N la;
    -	public static I18N lb;
    -	public static I18N lg;
    -	public static I18N li;
    -	public static I18N ln;
    -	public static I18N lo;
    -	public static I18N lt;
    -	public static I18N lu;
    -	public static I18N lv;
    -	public static I18N mg;
    -	public static I18N mh;
    -	public static I18N mi;
    -	public static I18N mk;
    -	public static I18N ml;
    -	public static I18N mn;
    -	public static I18N mo;
    -	public static I18N mr;
    -	public static I18N ms;
    -	public static I18N mt;
    -	public static I18N my;
    -	public static I18N na;
    -	public static I18N nb;
    -	public static I18N nd;
    -	public static I18N ne;
    -	public static I18N ng;
    -	public static I18N nl;
    -	public static I18N nn;
    -	public static I18N no;
    -	public static I18N nr;
    -	public static I18N nv;
    -	public static I18N ny;
    -	public static I18N oc;
    -	public static I18N oj;
    -	public static I18N om;
    -	public static I18N or;
    -	public static I18N os;
    -	public static I18N pa;
    -	public static I18N pi;
    -	public static I18N pl;
    -	public static I18N ps;
    -	public static I18N pt;
    -	public static I18N qu;
    -	public static I18N rm;
    -	public static I18N rn;
    -	public static I18N ro;
    -	public static I18N ru;
    -	public static I18N rw;
    -	public static I18N sa;
    -	public static I18N sc;
    -	public static I18N sd;
    -	public static I18N se;
    -	public static I18N sg;
    -	public static I18N si;
    -	public static I18N sk;
    -	public static I18N sl;
    -	public static I18N sm;
    -	public static I18N sn;
    -	public static I18N so;
    -	public static I18N sq;
    -	public static I18N sr;
    -	public static I18N ss;
    -	public static I18N st;
    -	public static I18N su;
    -	public static I18N sv;
    -	public static I18N sw;
    -	public static I18N ta;
    -	public static I18N te;
    -	public static I18N tg;
    -	public static I18N th;
    -	public static I18N ti;
    -	public static I18N tk;
    -	public static I18N tl;
    -	public static I18N tn;
    -	public static I18N to;
    -	public static I18N tr;
    -	public static I18N ts;
    -	public static I18N tt;
    -	public static I18N tw;
    -	public static I18N ty;
    -	public static I18N ug;
    -	public static I18N uk;
    -	public static I18N ur;
    -	public static I18N uz;
    -	public static I18N ve;
    -	public static I18N vi;
    -	public static I18N vo;
    -	public static I18N wa;
    -	public static I18N wo;
    -	public static I18N xh;
    -	public static I18N yi;
    -	public static I18N yo;
    -	public static I18N za;
    -	public static I18N zh;
    -	public static I18N zu;
    -	public static I18N none;
    +	private boolean empty = false;
    +	private Map map;
     
    +	public static I18N none;
     
    -	private boolean empty = false;
    -	private HashMap map;
    -	private Locale locale;
    -	private static boolean alreadyInitted = false;
    +	static {
    +		none = new I18N("default");
    +	}
     
     	/**
     	 * Determines if this I18N has values defined
    @@ -294,207 +110,11 @@ public void characters(String s) {
     	}
     	
     	/**
    -	 * Loads all I18N instances
    -	 * @return
    +	 * No longer required, maps are loaded on demand.
    +	 * Will be removed in next major release.
     	 */
    +	@Deprecated
     	public static void initialize() {
    -		if(alreadyInitted) {
    -			return;
    -		}
    -		
    -		alreadyInitted = true;
    -		
    -		aa = new I18N("aa");
    -		ab = new I18N("ab");
    -		ae = new I18N("ae");
    -		af = new I18N("af");
    -		ak = new I18N("ak");
    -		am = new I18N("am");
    -		an = new I18N("an");
    -		ar = new I18N("ar");
    -		as = new I18N("as");
    -		av = new I18N("av");
    -		ay = new I18N("ay");
    -		az = new I18N("az");
    -		ba = new I18N("ba");
    -		be = new I18N("be");
    -		bg = new I18N("bg");
    -		bh = new I18N("bh");
    -		bi = new I18N("bi");
    -		bm = new I18N("bm");
    -		bn = new I18N("bn");
    -		bo = new I18N("bo");
    -		br = new I18N("br");
    -		bs = new I18N("bs");
    -		ca = new I18N("ca");
    -		ce = new I18N("ce");
    -		ch = new I18N("ch");
    -		co = new I18N("co");
    -		cr = new I18N("cr");
    -		cs = new I18N("cs");
    -		cu = new I18N("cu");
    -		cv = new I18N("cv");
    -		cy = new I18N("cy");
    -		da = new I18N("da");
    -		de = new I18N("de");
    -		dv = new I18N("dv");
    -		dz = new I18N("dz");
    -		ee = new I18N("ee");
    -		el = new I18N("el");
    -		en = new I18N("en");
    -		eo = new I18N("eo");
    -		es = new I18N("es");
    -		et = new I18N("et");
    -		eu = new I18N("eu");
    -		fa = new I18N("fa");
    -		ff = new I18N("ff");
    -		fi = new I18N("fi");
    -		fj = new I18N("fj");
    -		fo = new I18N("fo");
    -		fr = new I18N("fr");
    -		fy = new I18N("fy");
    -		ga = new I18N("ga");
    -		gd = new I18N("gd");
    -		gl = new I18N("gl");
    -		gn = new I18N("gn");
    -		gu = new I18N("gu");
    -		gv = new I18N("gv");
    -		ha = new I18N("ha");
    -		he = new I18N("he");
    -		hi = new I18N("hi");
    -		ho = new I18N("ho");
    -		hr = new I18N("hr");
    -		ht = new I18N("ht");
    -		hu = new I18N("hu");
    -		hy = new I18N("hy");
    -		hz = new I18N("hz");
    -		ia = new I18N("ia");
    -		id = new I18N("id");
    -		ie = new I18N("ie");
    -		ig = new I18N("ig");
    -		ii = new I18N("ii");
    -		ik = new I18N("ik");
    -		in = new I18N("in");
    -		io = new I18N("io");
    -		is = new I18N("is");
    -		it = new I18N("it");
    -		iu = new I18N("iu");
    -		iw = new I18N("iw");
    -		ja = new I18N("ja");
    -		ji = new I18N("ji");
    -		jv = new I18N("jv");
    -		ka = new I18N("ka");
    -		kg = new I18N("kg");
    -		ki = new I18N("ki");
    -		kj = new I18N("kj");
    -		kk = new I18N("kk");
    -		kl = new I18N("kl");
    -		km = new I18N("km");
    -		kn = new I18N("kn");
    -		ko = new I18N("ko");
    -		kr = new I18N("kr");
    -		ks = new I18N("ks");
    -		ku = new I18N("ku");
    -		kv = new I18N("kv");
    -		kw = new I18N("kw");
    -		ky = new I18N("ky");
    -		la = new I18N("la");
    -		lb = new I18N("lb");
    -		lg = new I18N("lg");
    -		li = new I18N("li");
    -		ln = new I18N("ln");
    -		lo = new I18N("lo");
    -		lt = new I18N("lt");
    -		lu = new I18N("lu");
    -		lv = new I18N("lv");
    -		mg = new I18N("mg");
    -		mh = new I18N("mh");
    -		mi = new I18N("mi");
    -		mk = new I18N("mk");
    -		ml = new I18N("ml");
    -		mn = new I18N("mn");
    -		mo = new I18N("mo");
    -		mr = new I18N("mr");
    -		ms = new I18N("ms");
    -		mt = new I18N("mt");
    -		my = new I18N("my");
    -		na = new I18N("na");
    -		nb = new I18N("nb");
    -		nd = new I18N("nd");
    -		ne = new I18N("ne");
    -		ng = new I18N("ng");
    -		nl = new I18N("nl");
    -		nn = new I18N("nn");
    -		no = new I18N("no");
    -		nr = new I18N("nr");
    -		nv = new I18N("nv");
    -		ny = new I18N("ny");
    -		oc = new I18N("oc");
    -		oj = new I18N("oj");
    -		om = new I18N("om");
    -		or = new I18N("or");
    -		os = new I18N("os");
    -		pa = new I18N("pa");
    -		pi = new I18N("pi");
    -		pl = new I18N("pl");
    -		ps = new I18N("ps");
    -		pt = new I18N("pt");
    -		qu = new I18N("qu");
    -		rm = new I18N("rm");
    -		rn = new I18N("rn");
    -		ro = new I18N("ro");
    -		ru = new I18N("ru");
    -		rw = new I18N("rw");
    -		sa = new I18N("sa");
    -		sc = new I18N("sc");
    -		sd = new I18N("sd");
    -		se = new I18N("se");
    -		sg = new I18N("sg");
    -		si = new I18N("si");
    -		sk = new I18N("sk");
    -		sl = new I18N("sl");
    -		sm = new I18N("sm");
    -		sn = new I18N("sn");
    -		so = new I18N("so");
    -		sq = new I18N("sq");
    -		sr = new I18N("sr");
    -		ss = new I18N("ss");
    -		st = new I18N("st");
    -		su = new I18N("su");
    -		sv = new I18N("sv");
    -		sw = new I18N("sw");
    -		ta = new I18N("ta");
    -		te = new I18N("te");
    -		tg = new I18N("tg");
    -		th = new I18N("th");
    -		ti = new I18N("ti");
    -		tk = new I18N("tk");
    -		tl = new I18N("tl");
    -		tn = new I18N("tn");
    -		to = new I18N("to");
    -		tr = new I18N("tr");
    -		ts = new I18N("ts");
    -		tt = new I18N("tt");
    -		tw = new I18N("tw");
    -		ty = new I18N("ty");
    -		ug = new I18N("ug");
    -		uk = new I18N("uk");
    -		ur = new I18N("ur");
    -		uz = new I18N("uz");
    -		ve = new I18N("ve");
    -		vi = new I18N("vi");
    -		vo = new I18N("vo");
    -		wa = new I18N("wa");
    -		wo = new I18N("wo");
    -		xh = new I18N("xh");
    -		yi = new I18N("yi");
    -		yo = new I18N("yo");
    -		za = new I18N("za");
    -		zh = new I18N("zh");
    -		zu = new I18N("zu");
    -		none = new I18N("default");
    -		
    -		
     	}
     
     	/**
    @@ -503,198 +123,12 @@ public static void initialize() {
     	 * @return
     	 */
     	public static I18N getI18N (String iso639Code) {
    -		
    -		if(iso639Code.equals("aa")) return aa;
    -		if(iso639Code.equals("ab")) return ab;
    -		if(iso639Code.equals("ae")) return ae;
    -		if(iso639Code.equals("af")) return af;
    -		if(iso639Code.equals("ak")) return ak;
    -		if(iso639Code.equals("am")) return am;
    -		if(iso639Code.equals("an")) return an;
    -		if(iso639Code.equals("ar")) return ar;
    -		if(iso639Code.equals("as")) return as;
    -		if(iso639Code.equals("av")) return av;
    -		if(iso639Code.equals("ay")) return ay;
    -		if(iso639Code.equals("az")) return az;
    -		if(iso639Code.equals("ba")) return ba;
    -		if(iso639Code.equals("be")) return be;
    -		if(iso639Code.equals("bg")) return bg;
    -		if(iso639Code.equals("bh")) return bh;
    -		if(iso639Code.equals("bi")) return bi;
    -		if(iso639Code.equals("bm")) return bm;
    -		if(iso639Code.equals("bn")) return bn;
    -		if(iso639Code.equals("bo")) return bo;
    -		if(iso639Code.equals("br")) return br;
    -		if(iso639Code.equals("bs")) return bs;
    -		if(iso639Code.equals("ca")) return ca;
    -		if(iso639Code.equals("ce")) return ce;
    -		if(iso639Code.equals("ch")) return ch;
    -		if(iso639Code.equals("co")) return co;
    -		if(iso639Code.equals("cr")) return cr;
    -		if(iso639Code.equals("cs")) return cs;
    -		if(iso639Code.equals("cu")) return cu;
    -		if(iso639Code.equals("cv")) return cv;
    -		if(iso639Code.equals("cy")) return cy;
    -		if(iso639Code.equals("da")) return da;
    -		if(iso639Code.equals("de")) return de;
    -		if(iso639Code.equals("dv")) return dv;
    -		if(iso639Code.equals("dz")) return dz;
    -		if(iso639Code.equals("ee")) return ee;
    -		if(iso639Code.equals("el")) return el;
    -		if(iso639Code.equals("en")) return en;
    -		if(iso639Code.equals("eo")) return eo;
    -		if(iso639Code.equals("es")) return es;
    -		if(iso639Code.equals("et")) return et;
    -		if(iso639Code.equals("eu")) return eu;
    -		if(iso639Code.equals("fa")) return fa;
    -		if(iso639Code.equals("ff")) return ff;
    -		if(iso639Code.equals("fi")) return fi;
    -		if(iso639Code.equals("fj")) return fj;
    -		if(iso639Code.equals("fo")) return fo;
    -		if(iso639Code.equals("fr")) return fr;
    -		if(iso639Code.equals("fy")) return fy;
    -		if(iso639Code.equals("ga")) return ga;
    -		if(iso639Code.equals("gd")) return gd;
    -		if(iso639Code.equals("gl")) return gl;
    -		if(iso639Code.equals("gn")) return gn;
    -		if(iso639Code.equals("gu")) return gu;
    -		if(iso639Code.equals("gv")) return gv;
    -		if(iso639Code.equals("ha")) return ha;
    -		if(iso639Code.equals("he")) return he;
    -		if(iso639Code.equals("hi")) return hi;
    -		if(iso639Code.equals("ho")) return ho;
    -		if(iso639Code.equals("hr")) return hr;
    -		if(iso639Code.equals("ht")) return ht;
    -		if(iso639Code.equals("hu")) return hu;
    -		if(iso639Code.equals("hy")) return hy;
    -		if(iso639Code.equals("hz")) return hz;
    -		if(iso639Code.equals("ia")) return ia;
    -		if(iso639Code.equals("id")) return id;
    -		if(iso639Code.equals("ie")) return ie;
    -		if(iso639Code.equals("ig")) return ig;
    -		if(iso639Code.equals("ii")) return ii;
    -		if(iso639Code.equals("ik")) return ik;
    -		if(iso639Code.equals("in")) return in;
    -		if(iso639Code.equals("io")) return io;
    -		if(iso639Code.equals("is")) return is;
    -		if(iso639Code.equals("it")) return it;
    -		if(iso639Code.equals("iu")) return iu;
    -		if(iso639Code.equals("iw")) return iw;
    -		if(iso639Code.equals("ja")) return ja;
    -		if(iso639Code.equals("ji")) return ji;
    -		if(iso639Code.equals("jv")) return jv;
    -		if(iso639Code.equals("ka")) return ka;
    -		if(iso639Code.equals("kg")) return kg;
    -		if(iso639Code.equals("ki")) return ki;
    -		if(iso639Code.equals("kj")) return kj;
    -		if(iso639Code.equals("kk")) return kk;
    -		if(iso639Code.equals("kl")) return kl;
    -		if(iso639Code.equals("km")) return km;
    -		if(iso639Code.equals("kn")) return kn;
    -		if(iso639Code.equals("ko")) return ko;
    -		if(iso639Code.equals("kr")) return kr;
    -		if(iso639Code.equals("ks")) return ks;
    -		if(iso639Code.equals("ku")) return ku;
    -		if(iso639Code.equals("kv")) return kv;
    -		if(iso639Code.equals("kw")) return kw;
    -		if(iso639Code.equals("ky")) return ky;
    -		if(iso639Code.equals("la")) return la;
    -		if(iso639Code.equals("lb")) return lb;
    -		if(iso639Code.equals("lg")) return lg;
    -		if(iso639Code.equals("li")) return li;
    -		if(iso639Code.equals("ln")) return ln;
    -		if(iso639Code.equals("lo")) return lo;
    -		if(iso639Code.equals("lt")) return lt;
    -		if(iso639Code.equals("lu")) return lu;
    -		if(iso639Code.equals("lv")) return lv;
    -		if(iso639Code.equals("mg")) return mg;
    -		if(iso639Code.equals("mh")) return mh;
    -		if(iso639Code.equals("mi")) return mi;
    -		if(iso639Code.equals("mk")) return mk;
    -		if(iso639Code.equals("ml")) return ml;
    -		if(iso639Code.equals("mn")) return mn;
    -		if(iso639Code.equals("mo")) return mo;
    -		if(iso639Code.equals("mr")) return mr;
    -		if(iso639Code.equals("ms")) return ms;
    -		if(iso639Code.equals("mt")) return mt;
    -		if(iso639Code.equals("my")) return my;
    -		if(iso639Code.equals("na")) return na;
    -		if(iso639Code.equals("nb")) return nb;
    -		if(iso639Code.equals("nd")) return nd;
    -		if(iso639Code.equals("ne")) return ne;
    -		if(iso639Code.equals("ng")) return ng;
    -		if(iso639Code.equals("nl")) return nl;
    -		if(iso639Code.equals("nn")) return nn;
    -		if(iso639Code.equals("no")) return no;
    -		if(iso639Code.equals("nr")) return nr;
    -		if(iso639Code.equals("nv")) return nv;
    -		if(iso639Code.equals("ny")) return ny;
    -		if(iso639Code.equals("oc")) return oc;
    -		if(iso639Code.equals("oj")) return oj;
    -		if(iso639Code.equals("om")) return om;
    -		if(iso639Code.equals("or")) return or;
    -		if(iso639Code.equals("os")) return os;
    -		if(iso639Code.equals("pa")) return pa;
    -		if(iso639Code.equals("pi")) return pi;
    -		if(iso639Code.equals("pl")) return pl;
    -		if(iso639Code.equals("ps")) return ps;
    -		if(iso639Code.equals("pt")) return pt;
    -		if(iso639Code.equals("qu")) return qu;
    -		if(iso639Code.equals("rm")) return rm;
    -		if(iso639Code.equals("rn")) return rn;
    -		if(iso639Code.equals("ro")) return ro;
    -		if(iso639Code.equals("ru")) return ru;
    -		if(iso639Code.equals("rw")) return rw;
    -		if(iso639Code.equals("sa")) return sa;
    -		if(iso639Code.equals("sc")) return sc;
    -		if(iso639Code.equals("sd")) return sd;
    -		if(iso639Code.equals("se")) return se;
    -		if(iso639Code.equals("sg")) return sg;
    -		if(iso639Code.equals("si")) return si;
    -		if(iso639Code.equals("sk")) return sk;
    -		if(iso639Code.equals("sl")) return sl;
    -		if(iso639Code.equals("sm")) return sm;
    -		if(iso639Code.equals("sn")) return sn;
    -		if(iso639Code.equals("so")) return so;
    -		if(iso639Code.equals("sq")) return sq;
    -		if(iso639Code.equals("sr")) return sr;
    -		if(iso639Code.equals("ss")) return ss;
    -		if(iso639Code.equals("st")) return st;
    -		if(iso639Code.equals("su")) return su;
    -		if(iso639Code.equals("sv")) return sv;
    -		if(iso639Code.equals("sw")) return sw;
    -		if(iso639Code.equals("ta")) return ta;
    -		if(iso639Code.equals("te")) return te;
    -		if(iso639Code.equals("tg")) return tg;
    -		if(iso639Code.equals("th")) return th;
    -		if(iso639Code.equals("ti")) return ti;
    -		if(iso639Code.equals("tk")) return tk;
    -		if(iso639Code.equals("tl")) return tl;
    -		if(iso639Code.equals("tn")) return tn;
    -		if(iso639Code.equals("to")) return to;
    -		if(iso639Code.equals("tr")) return tr;
    -		if(iso639Code.equals("ts")) return ts;
    -		if(iso639Code.equals("tt")) return tt;
    -		if(iso639Code.equals("tw")) return tw;
    -		if(iso639Code.equals("ty")) return ty;
    -		if(iso639Code.equals("ug")) return ug;
    -		if(iso639Code.equals("uk")) return uk;
    -		if(iso639Code.equals("ur")) return ur;
    -		if(iso639Code.equals("uz")) return uz;
    -		if(iso639Code.equals("ve")) return ve;
    -		if(iso639Code.equals("vi")) return vi;
    -		if(iso639Code.equals("vo")) return vo;
    -		if(iso639Code.equals("wa")) return wa;
    -		if(iso639Code.equals("wo")) return wo;
    -		if(iso639Code.equals("xh")) return xh;
    -		if(iso639Code.equals("yi")) return yi;
    -		if(iso639Code.equals("yo")) return yo;
    -		if(iso639Code.equals("za")) return za;
    -		if(iso639Code.equals("zh")) return zh;
    -		if(iso639Code.equals("zu")) return zu;
    -
    -		return none;
    -		
    +		I18N code = codes.get(iso639Code);
    +		if (code == null) {
    +			code = new I18N(iso639Code);
    +			codes.put(iso639Code, code);
    +		}
    +		return code;
     	}
     
     	/**
    diff --git a/TotalCrossSDK/src/main/java/totalcross/xml/soap/SOAP.java b/TotalCrossSDK/src/main/java/totalcross/xml/soap/SOAP.java
    index f561d48e45..0358de48fa 100644
    --- a/TotalCrossSDK/src/main/java/totalcross/xml/soap/SOAP.java
    +++ b/TotalCrossSDK/src/main/java/totalcross/xml/soap/SOAP.java
    @@ -18,9 +18,6 @@
     import totalcross.util.ElementNotFoundException;
     import totalcross.util.IntHashtable;
     import totalcross.util.Vector;
    -import totalcross.util.zip.CompressedStream;
    -import totalcross.util.zip.GZipStream;
    -import totalcross.util.zip.ZLibStream;
     import totalcross.xml.DumpXml;
     import totalcross.xml.SyntaxException;
     import totalcross.xml.XmlTokenizer;
    @@ -109,6 +106,7 @@ public class SOAP // guich@570_34
        * This is a ready-only flag, set during the execute method, and changing its 
        * value has no effect.
        */
    +  @Deprecated
       public boolean wasCompressionUsed; // guich@tc114_89
       /*
        * luciana@570_45 - Added these attributes and a constructor that only
    @@ -658,9 +656,7 @@ public void execute() throws SOAPException {
           httpOptions.openTimeOut = openTimeout;
           httpOptions.writeTimeOut = writeTimeout;
           httpOptions.httpType = HttpStream.POST; //doPost = true;
    -      if (!disableEncoding) {
    -        httpOptions.postHeaders.put("Accept-Encoding", "deflate;q=1.0, gzip;q=0.5"); // flsobral@tc110_77: zlib encoding is preferred over gzip encoding.
    -      }
    +      httpOptions.disableEncoding = disableEncoding;
           httpOptions.postHeaders.put("Content-Type", "text/xml; charset=utf-8");
           httpOptions.postHeaders.put("SOAPAction", "\"" + namespace + (!namespace.endsWith("/") ? "/" : "") + mtd + "\""); // flsobral@tc100b5_48: only add a trailing slash if the namespace does not have one already.
           httpOptions.postPrefix = "" + prefix
    @@ -684,18 +680,8 @@ public void execute() throws SOAPException {
           Stream receivedStream;
           int initialSize = hs.contentLength > 0 ? hs.contentLength : 1024;
           if (hs.contentEncoding != null) {
    -        wasCompressionUsed = false;
    -        if (hs.contentEncoding.equalsIgnoreCase("deflate")) {
    -          ZLibStream zs = new ZLibStream(hs, CompressedStream.INFLATE);
    -          receivedStream = zs;
    -          wasCompressionUsed = true;
    -        } else if (hs.contentEncoding.equalsIgnoreCase("gzip")) {
    -          GZipStream zs = new GZipStream(hs, CompressedStream.INFLATE); // flsobral@tc112_35: Better performance with GZipStream instead of GZip.
    -          receivedStream = zs;
    -          wasCompressionUsed = true;
    -        } else {
    -          throw new SOAPException("Unsupported encoding: " + hs.contentEncoding);
    -        }
    +        receivedStream = hs;
    +        wasCompressionUsed = true;
           } else {
             //flsobral@tc110_73: Use ByteArrayStream if the content is already encoded, if the length is unknown, or if the length is known and lower than 70k. (zlib requires at least 65k to run, so we'll only use it when reading more than 70k.)
             boolean useBBAS = (hs.contentLength >= -1 && hs.contentLength <= 70000) || Vm.getFreeMemory() < 1024 * 1024;
    @@ -710,7 +696,7 @@ public void execute() throws SOAPException {
             }
           }
           if (debug) {
    -        if (receivedStream instanceof CompressedStream) {
    +        if (hs.contentEncoding != null) {
               CompressedByteArrayStream bbas = new CompressedByteArrayStream();
               bbas.readFully(receivedStream, 5, initialSize);
               receivedStream.close();
    diff --git a/TotalCrossVM/CMakeLists.txt b/TotalCrossVM/CMakeLists.txt
    index fdd50ad614..f73b8479c5 100644
    --- a/TotalCrossVM/CMakeLists.txt
    +++ b/TotalCrossVM/CMakeLists.txt
    @@ -2,10 +2,30 @@
     #
     # SPDX-License-Identifier: LGPL-2.1-only
     
    -cmake_minimum_required(VERSION 3.5)
    +cmake_minimum_required(VERSION 3.11)
    +
    +include(FetchContent)
     
     # set the project name
    -project(TCVM VERSION 7.0.2)
    +project(tcvm VERSION 7.2.0)
    +
    +if(MSVC)
    +  if (${CMAKE_GENERATOR} STREQUAL "Visual Studio 9 2008 Pocket PC 2003 (ARMV4)")
    +    set(TARGET_WINCE ON)
    +  else()
    +    set(TARGET_WIN32 ON)
    +  endif()
    +elseif(DEFINED ANDROID_ABI)
    +  set(TARGET_ANDROID ON)
    +elseif(CMAKE_GENERATOR STREQUAL Xcode)
    +  set(TARGET_XCODE ON)
    +elseif(APPLE)
    +  set(TARGET_MACOSX ON)
    +elseif(UNIX)
    +  set(TARGET_UNIX ON)
    +else()
    +  message(FATAL_ERROR "Unable to detect target platform")
    +endif()
     
     if(NOT DEFINED BUILD_SHARED_LIBS AND NOT DEFINED TCVM_SHARED)
       if(CMAKE_GENERATOR STREQUAL Xcode)
    @@ -21,32 +41,195 @@ if (NOT DEFINED USE_SKIA)
       set(USE_SKIA ON)
     endif()
     
    +if (NOT DEFINED SKIA_AUTO_FETCH)
    +  set(SKIA_AUTO_FETCH ON)
    +endif()
    +
    +if (NOT DEFINED SQLITE3_AUTO_FETCH)
    +  set(SQLITE3_AUTO_FETCH ON)
    +endif()
    +
    +if (NOT DEFINED MBEDTLS_AUTO_FETCH)
    +  set(MBEDTLS_AUTO_FETCH ON)
    +endif()
    +
    +if (NOT DEFINED ZLIBNG_AUTO_FETCH)
    +  set(ZLIBNG_AUTO_FETCH ON)
    +endif()
    +
    +if (NOT DEFINED LIBPNG_AUTO_FETCH)
    +  set(LIBPNG_AUTO_FETCH ON)
    +endif()
    +
     if(TCVM_SHARED)
       add_library(tcvm SHARED)
     else()
       add_library(tcvm STATIC)
     endif()
     
    +# Force using madler zlib on wince instead of zlib-ng, which isn't supported
    +if(NOT DEFINED USE_MADLER_ZLIB)
    +  if(TARGET_WINCE)
    +    set(USE_MADLER_ZLIB ON)
    +  endif()
    +endif()
    +
    +# Support defining PNG_ARM_NEON_OPT externally
    +if(DEFINED PNG_ARM_NEON_OPT)
    +  add_definitions(-DPNG_ARM_NEON_OPT=${PNG_ARM_NEON_OPT})
    +endif(DEFINED PNG_ARM_NEON_OPT)
    +
    +# Find native dependencies from totalcross-depot-tools
    +set(TCVM_DEPOT_TOOLS_DIR "${CMAKE_SOURCE_DIR}/deps/totalcross-depot-tools" CACHE PATH "totalcross-depot-tools checkout")
    +
    +if(NOT EXISTS "${TCVM_DEPOT_TOOLS_DIR}/deps.yml")
    +  find_program(TCVM_BASH_EXECUTABLE bash)
    +  if(NOT TCVM_BASH_EXECUTABLE)
    +    message(FATAL_ERROR "Unable to fetch totalcross-depot-tools because 'bash' was not found")
    +  endif()
    +
    +  execute_process(
    +    COMMAND "${CMAKE_COMMAND}" -E env
    +      "TOTALCROSS_DEPOT_TOOLS_DIR=${TCVM_DEPOT_TOOLS_DIR}"
    +      "${TCVM_BASH_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/fetch-depot-tools.sh"
    +    WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
    +    RESULT_VARIABLE TCVM_DEPOT_TOOLS_FETCH_RESULT
    +    OUTPUT_VARIABLE TCVM_DEPOT_TOOLS_FETCH_STDOUT
    +    ERROR_VARIABLE TCVM_DEPOT_TOOLS_FETCH_STDERR
    +  )
    +
    +  if(NOT TCVM_DEPOT_TOOLS_FETCH_RESULT EQUAL 0)
    +    message(FATAL_ERROR
    +      "Failed to fetch totalcross-depot-tools.\n"
    +      "stdout:\n${TCVM_DEPOT_TOOLS_FETCH_STDOUT}\n"
    +      "stderr:\n${TCVM_DEPOT_TOOLS_FETCH_STDERR}"
    +    )
    +  endif()
    +endif()
    +
    +set(CMAKE_MODULE_PATH
    +  "${TCVM_DEPOT_TOOLS_DIR}/zlib-ng/cmake"
    +  "${TCVM_DEPOT_TOOLS_DIR}/libpng/cmake"
    +  "${TCVM_DEPOT_TOOLS_DIR}/sqlite3/cmake"
    +  "${TCVM_DEPOT_TOOLS_DIR}/mbedtls/cmake"
    +  "${TCVM_DEPOT_TOOLS_DIR}/skia/cmake"
    +  "${CMAKE_SOURCE_DIR}/modules"
    +)
    +
    +set(SKIA_DIR "${TCVM_DEPOT_TOOLS_DIR}/skia/local" CACHE PATH "Skia directory" FORCE)
    +
    +foreach(TCVM_DEP_CACHE_VAR
    +    ZLIB_DIR
    +    ZLIB_INCLUDE_DIR
    +    ZLIB_LIBRARY
    +    PNG_DIR
    +    PNG_INCLUDE_DIR
    +    PNG_LIBRARY
    +    SQLITE3_DIR
    +    SQLite3_INCLUDE_DIR
    +    SQLite3_LIBRARY
    +    MBEDTLS_DIR
    +    MbedTLS_INCLUDE_DIR
    +    MbedTLS_LIBRARY
    +    MbedCrypto_LIBRARY
    +    MbedX509_LIBRARY
    +    SKIA_CONFIG_INCLUDE_DIR
    +    SKIA_CORE_INCLUDE_DIR
    +    SKIA_UTILS_INCLUDE_DIR
    +    SKIA_EFFECTS_INCLUDE_DIR
    +    SKIA_GPU_INCLUDE_DIR
    +    SKIA_GPU2_INCLUDE_DIR
    +    SKIA_LIBRARY_DIRS
    +    SKIA_LIBRARIES)
    +  unset(${TCVM_DEP_CACHE_VAR} CACHE)
    +  unset(${TCVM_DEP_CACHE_VAR})
    +endforeach()
    +
    +if(NOT USE_MADLER_ZLIB)
    +  include("${TCVM_DEPOT_TOOLS_DIR}/zlib-ng/cmake/AutoFetchZlibNg.cmake")
    +  if(ZLIBNG_AUTO_FETCH)
    +    tcvm_auto_fetch_zlibng()
    +  endif()
    +endif()
    +
    +# Find libpng
    +if(NOT TARGET_WINCE)
    +  include("${TCVM_DEPOT_TOOLS_DIR}/libpng/cmake/AutoFetchLibPng.cmake")
    +  if(LIBPNG_AUTO_FETCH)
    +    tcvm_auto_fetch_libpng()
    +  endif()
    +endif()
    +
    +# Find SQLite3
    +include("${TCVM_DEPOT_TOOLS_DIR}/sqlite3/cmake/AutoFetchSQLite3.cmake")
    +if(SQLITE3_AUTO_FETCH)
    +  tcvm_auto_fetch_sqlite3()
    +endif()
    +find_package(SQLite3 REQUIRED)
    +set(SQLITE3_LIBRARIES "${SQLite3_LIBRARY}")
    +set(SQLITE3_INCLUDE_DIRS "${SQLite3_INCLUDE_DIR}")
    +get_filename_component(SQLITE3_LIBRARY_DIR "${SQLite3_LIBRARY}" DIRECTORY)
    +include_directories("${SQLite3_INCLUDE_DIR}")
    +
    +# Find mbedTLS
    +if(NOT TARGET_WINCE)
    +  include("${TCVM_DEPOT_TOOLS_DIR}/mbedtls/cmake/AutoFetchMbedTLS.cmake")
    +  if(MBEDTLS_AUTO_FETCH)
    +    tcvm_auto_fetch_mbedtls()
    +  endif()
    +  find_package(MbedTLS REQUIRED)
    +  set(MBEDTLS_LIBRARIES "${MbedTLS_LIBRARY};${MbedX509_LIBRARY};${MbedCrypto_LIBRARY}")
    +  set(MBEDTLS_INCLUDE_DIRS "${MbedTLS_INCLUDE_DIR}")
    +  get_filename_component(MBEDTLS_LIBRARY_DIR "${MbedTLS_LIBRARY}" DIRECTORY)
    +endif()
    +
     add_subdirectory(third_party) 
     
    +if(CMAKE_GENERATOR STREQUAL Xcode)
    +  target_link_libraries(tcvm PRIVATE zlibstatic png_static SQLite::SQLite3)
    +  if(NOT TARGET_WINCE)
    +    target_link_libraries(tcvm PRIVATE
    +      MbedTLS::mbedtls
    +      MbedTLS::mbedx509
    +      MbedTLS::mbedcrypto
    +    )
    +  endif()
    +else()
    +  target_link_libraries(tcvm zlibstatic)
    +  target_link_libraries(tcvm png_static)
    +  target_link_libraries(tcvm ${SQLITE3_LIBRARIES})
    +  if(NOT TARGET_WINCE)
    +    target_link_libraries(tcvm MbedTLS::mbedtls)
    +  endif()
    +endif()
    +
     # Find SDL2 and Skia
    -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/modules")
    +include("${TCVM_DEPOT_TOOLS_DIR}/skia/cmake/AutoFetchSkia.cmake")
     
     # Find SDL2
     IF (NOT (DEFINED ANDROID_ABI OR MSVC OR CMAKE_GENERATOR STREQUAL Xcode))
    -  find_package(SDL2 REQUIRED)
    +  find_package(SDL2 2.0.12 REQUIRED)
       include_directories(${SDL2_INCLUDE_DIR})
     ENDIF (NOT (DEFINED ANDROID_ABI OR MSVC OR CMAKE_GENERATOR STREQUAL Xcode))
     
     # Find Skia
     if(NOT MSVC AND USE_SKIA)
    +  if(SKIA_AUTO_FETCH)
    +    tcvm_auto_fetch_skia()
    +  endif()
       find_package(Skia REQUIRED)
    +  if(CMAKE_GENERATOR STREQUAL Xcode AND NOT TARGET skia_static)
    +    add_library(skia_static STATIC IMPORTED GLOBAL)
    +    set_target_properties(skia_static PROPERTIES
    +      IMPORTED_LOCATION "${SKIA_LIBRARIES}"
    +      INTERFACE_INCLUDE_DIRECTORIES "${SKIA_INCLUDE_DIRS}"
    +    )
    +  endif()
       include_directories(${SKIA_INCLUDE_DIRS})
       add_definitions(-DUSE_SKIA)
     endif(NOT MSVC AND USE_SKIA)
     
     set(TC_SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}/src")
    -set(LB_SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}/src/litebase")
     set(IOS_BUNDLE_SRCDIR "${CMAKE_CURRENT_SOURCE_DIR}/xcode")
     
     set(SOURCES
    @@ -105,18 +288,6 @@ set(SOURCES
         ${TC_SRCDIR}/minizip/unzip.c
         ${TC_SRCDIR}/minizip/zip.c
     
    -    ${TC_SRCDIR}/zlib/adler32.c
    -    ${TC_SRCDIR}/zlib/compress.c
    -    ${TC_SRCDIR}/zlib/crc32.c
    -    ${TC_SRCDIR}/zlib/deflate.c
    -    ${TC_SRCDIR}/zlib/infback.c
    -    ${TC_SRCDIR}/zlib/inffast.c
    -    ${TC_SRCDIR}/zlib/inflate.c
    -    ${TC_SRCDIR}/zlib/inftrees.c
    -    ${TC_SRCDIR}/zlib/trees.c
    -    ${TC_SRCDIR}/zlib/uncompr.c
    -    ${TC_SRCDIR}/zlib/zutil.c
    -
         ${TC_SRCDIR}/ras/ras_Utils.c
     
         ${TC_SRCDIR}/nm/map/GoogleMaps.c
    @@ -156,7 +327,7 @@ set(SOURCES
         ${TC_SRCDIR}/nm/net/ConnectionManager.c
     
         ${TC_SRCDIR}/nm/pim/POutlook.c
    -
    +    
         ${TC_SRCDIR}/nm/notification/notification.c
         
         ${TC_SRCDIR}/nm/phone/Dial.c
    @@ -184,6 +355,7 @@ set(SOURCES
         ${TC_SRCDIR}/nm/ui/media_Camera.c
         ${TC_SRCDIR}/nm/ui/YoutubePlayer.c
         ${TC_SRCDIR}/nm/ui/Window.c
    +    ${TC_SRCDIR}/nm/ui/dialog_FilePicker.c
     
         ${TC_SRCDIR}/nm/util/concurrent_Lock.c
         ${TC_SRCDIR}/nm/util/zip_ZLib.c
    @@ -194,19 +366,6 @@ set(SOURCES
         ${TC_SRCDIR}/nm/util/zip/ZipFile.c
         ${TC_SRCDIR}/nm/util/zip/ZipEntry.c
     
    -    ${TC_SRCDIR}/png/pngrutil.c
    -    ${TC_SRCDIR}/png/pngerror.c
    -    ${TC_SRCDIR}/png/pngget.c
    -    ${TC_SRCDIR}/png/PngLoader.c
    -    ${TC_SRCDIR}/png/pngmem.c
    -    ${TC_SRCDIR}/png/pngpread.c
    -    ${TC_SRCDIR}/png/pngread.c
    -    ${TC_SRCDIR}/png/pngrio.c
    -    ${TC_SRCDIR}/png/pngrtran.c
    -    ${TC_SRCDIR}/png/png.c
    -    ${TC_SRCDIR}/png/pngset.c
    -    ${TC_SRCDIR}/png/pngtrans.c
    -
         ${TC_SRCDIR}/jpeg/jcapimin.c
         ${TC_SRCDIR}/jpeg/jcapistd.c
         ${TC_SRCDIR}/jpeg/jccoefct.c
    @@ -298,40 +457,6 @@ if(DEFINED SKIA_LIBRARIES)
       )
     endif(DEFINED SKIA_LIBRARIES)
     
    -if(NOT MSVC)
    -  set(LB_SOURCES
    -    ${LB_SRCDIR}/lbFile.c
    -    ${LB_SRCDIR}/PlainDB.c
    -    ${LB_SRCDIR}/TCVMLib.c
    -    ${LB_SRCDIR}/Litebase.c
    -    ${LB_SRCDIR}/ResultSet.c
    -    ${LB_SRCDIR}/NativeMethods.c
    -    ${LB_SRCDIR}/Table.c
    -    ${LB_SRCDIR}/LitebaseGlobals.c
    -    ${LB_SRCDIR}/nativeProcAddressesLB.c
    -    ${LB_SRCDIR}/Key.c
    -    ${LB_SRCDIR}/Node.c
    -    ${LB_SRCDIR}/Index.c
    -    ${LB_SRCDIR}/SQLValue.c
    -    ${LB_SRCDIR}/MarkBits.c
    -    ${LB_SRCDIR}/MemoryFile.c
    -    ${LB_SRCDIR}/NormalFile.c
    -    ${LB_SRCDIR}/PreparedStatement.c
    -    ${LB_SRCDIR}/UtilsLB.c
    -
    -    ${LB_SRCDIR}/parser/LitebaseLex.c
    -    ${LB_SRCDIR}/parser/LitebaseMessage.c
    -    ${LB_SRCDIR}/parser/LitebaseParser.c
    -    ${LB_SRCDIR}/parser/SQLBooleanClause.c
    -    ${LB_SRCDIR}/parser/SQLBooleanClauseTree.c
    -    ${LB_SRCDIR}/parser/SQLColumnListClause.c
    -    ${LB_SRCDIR}/parser/SQLDeleteStatement.c
    -    ${LB_SRCDIR}/parser/SQLInsertStatement.c
    -    ${LB_SRCDIR}/parser/SQLSelectStatement.c
    -    ${LB_SRCDIR}/parser/SQLUpdateStatement.c
    -    )
    -endif(NOT MSVC)
    -
     if(CMAKE_GENERATOR STREQUAL Xcode)
       set(TCVM_iOS_FILES
         ${IOS_BUNDLE_SRCDIR}/tcvm/libtcvm.h
    @@ -393,10 +518,15 @@ if(DEFINED ANDROID_ABI)
       )
     endif(DEFINED ANDROID_ABI)
     
    -if(MSVC AND ${CMAKE_GENERATOR} STREQUAL "Visual Studio 9 2008 Pocket PC 2003 (ARMV4)")
    +if(TARGET_WINCE)
       set(TC_WCE_SOURCES
         ${TC_SRCDIR}/util/win/aygshellLib.c
       )
    +else()
    +  set(SOURCES
    +    ${SOURCES}
    +    ${TC_SRCDIR}/nm/net/ssl/SSLContextMbedtls.c
    +  )
     endif()
     
     include_directories(${TC_SRCDIR})
    @@ -408,8 +538,6 @@ include_directories(${TC_SRCDIR}/event/android)
     include_directories(${TC_SRCDIR}/init)
     include_directories(${TC_SRCDIR}/init/android)
     include_directories(${TC_SRCDIR}/jpeg)
    -include_directories(${TC_SRCDIR}/litebase)
    -include_directories(${TC_SRCDIR}/litebase/parser)
     include_directories(${TC_SRCDIR}/minizip)
     include_directories(${TC_SRCDIR}/nm)
     include_directories(${TC_SRCDIR}/nm/crypto)
    @@ -428,7 +556,6 @@ include_directories(${TC_SRCDIR}/nm/ui)
     include_directories(${TC_SRCDIR}/nm/ui/android)
     include_directories(${TC_SRCDIR}/nm/xml)
     include_directories(${TC_SRCDIR}/palmdb)
    -include_directories(${TC_SRCDIR}/png)
     include_directories(${TC_SRCDIR}/ras)
     include_directories(${TC_SRCDIR}/scanner)
     include_directories(${TC_SRCDIR}/scanner/android)
    @@ -436,11 +563,12 @@ include_directories(${TC_SRCDIR}/sync)
     include_directories(${TC_SRCDIR}/tcvm)
     include_directories(${TC_SRCDIR}/util)
     include_directories(${TC_SRCDIR}/util/android)
    -include_directories(${TC_SRCDIR}/zlib)
     
     if(MSVC)
       include_directories(${TC_SRCDIR}/nm/qrcode/win)
    -  include_directories(${TC_SRCDIR}/core/wince)
    +  if(TARGET_WINCE)
    +    include_directories(${TC_SRCDIR}/core/wince) # Only include this folder for WinCE!
    +  endif()
     else()
       include_directories(${TC_SRCDIR}/nm/qrcode)
       include_directories(${TC_SRCDIR}/nm/lang/posix)
    @@ -467,21 +595,18 @@ endif(DEFINED SKIA_LIBRARIES)
     # add_definitions
     if(DEFINED ANDROID_ABI)
       add_definitions(-DANDROID)
    -  if ("${ANDROID_ABI}" MATCHES "arm64-v8a")
    +  if ("${ANDROID_ABI}" MATCHES "arm64-v8a" OR USE_SKIA)
     	  add_definitions(-DFORCE_LIBC_ALLOC)
       endif()
    -elseif(MSVC AND ${CMAKE_GENERATOR} STREQUAL "Visual Studio 9 2008 Pocket PC 2003 (ARMV4)")
    +elseif(TARGET_WINCE)
       add_definitions(-DUNICODE -D_WIN32_WCE=$(CEVER) -D_MSC_VER=1200 -D_REENTRANT -DLITTLE_ENDIAN)
     elseif(MSVC)
       add_definitions(-DFORCE_LIBC_ALLOC -DWIN32 -D_REENTRANT -DHAVE_MREMAP=0 -DLITTLE_ENDIAN)
     else()
       if(CMAKE_GENERATOR STREQUAL Xcode)
         add_definitions(-Ddarwin -DSK_GL)
    -    set(CMAKE_STATIC_LINKER_FLAGS ${CMAKE_STATIC_LINKER_FLAGS} "-lskia")
    -    set(CMAKE_XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS ${SKIA_LIBRARY_DIRS})
    -    set(CMAKE_XCODE_ATTRIBUTE_LINK_BINARIES_WITH_LIBRARIES ${SKIA_LIBRARIES})
         set(CMAKE_OSX_SYSROOT iphoneos)
    -    set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)")
    +    set(CMAKE_OSX_ARCHITECTURES "arm64")
       else()
         add_definitions(-DHEADLESS)
       endif()
    @@ -489,11 +614,6 @@ else()
     endif(DEFINED ANDROID_ABI)
     add_definitions(-DTOTALCROSS -DTC_EXPORTS)
     
    -# Support defining PNG_ARM_NEON_OPT externally
    -if(DEFINED PNG_ARM_NEON_OPT)
    -  add_definitions(-DPNG_ARM_NEON_OPT=${PNG_ARM_NEON_OPT})
    -endif(DEFINED PNG_ARM_NEON_OPT)
    -
     if(APPLE AND CMAKE_BUILD_TYPE MATCHES "Debug")
       add_definitions(-DCURRENT_DEBUG_PATH="${CMAKE_CURRENT_BINARY_DIR}")
     endif(APPLE AND CMAKE_BUILD_TYPE MATCHES "Debug")
    @@ -501,7 +621,7 @@ endif(APPLE AND CMAKE_BUILD_TYPE MATCHES "Debug")
     link_directories(${PROJECT_SOURCE_DIR})
     
     # add the library
    -set(LIBRARY_ARGS ${SOURCES} ${TC_WCE_SOURCES} ${TC_MSVC_SOURCES} ${TC_NOT_MSVC_SOURCES} ${LB_SOURCES} ${TC_SKIA_SOURCES} ${TC_SDL_SOURCES} ${TC_ANDROID_SOURCES} ${TCVM_iOS_FILES})
    +set(LIBRARY_ARGS ${SOURCES} ${TC_WCE_SOURCES} ${TC_MSVC_SOURCES} ${TC_NOT_MSVC_SOURCES} ${TC_SKIA_SOURCES} ${TC_SDL_SOURCES} ${TC_ANDROID_SOURCES} ${TCVM_iOS_FILES})
     target_sources(tcvm PUBLIC ${LIBRARY_ARGS})
     
     # Set C/CXX flags
    @@ -537,6 +657,28 @@ else()
     endif()
     # XCode Properties
     if(CMAKE_GENERATOR STREQUAL Xcode)
    +  # Fold prebuilt native dependencies into libtcvm.a at archive creation time.
    +  set(TCVM_XCODE_ARCHIVE_DEPENDENCIES
    +    "${SKIA_LIBRARIES}"
    +    "${PNG_LIBRARY}"
    +    "${ZLIB_LIBRARY}"
    +    "${SQLITE3_LIBRARIES}"
    +    "${MBEDTLS_LIBRARIES}"
    +  )
    +  list(REMOVE_DUPLICATES TCVM_XCODE_ARCHIVE_DEPENDENCIES)
    +
    +  set(TCVM_XCODE_OTHER_LIBTOOLFLAGS "")
    +  foreach(TCVM_XCODE_ARCHIVE_DEPENDENCY ${TCVM_XCODE_ARCHIVE_DEPENDENCIES})
    +    if(TCVM_XCODE_ARCHIVE_DEPENDENCY)
    +      if(NOT EXISTS "${TCVM_XCODE_ARCHIVE_DEPENDENCY}")
    +        message(FATAL_ERROR
    +          "Unable to locate the iOS static dependency '${TCVM_XCODE_ARCHIVE_DEPENDENCY}' "
    +          "that must be folded into libtcvm.a"
    +        )
    +      endif()
    +      string(APPEND TCVM_XCODE_OTHER_LIBTOOLFLAGS " \"${TCVM_XCODE_ARCHIVE_DEPENDENCY}\"")
    +    endif()
    +  endforeach()
       
       # adds $(inherited) to HEADER_SEARCH_PATHS in order to get pod libraries headers
       get_target_property(INCLUDED_HEADERS tcvm INCLUDE_DIRECTORIES)
    @@ -548,11 +690,16 @@ if(CMAKE_GENERATOR STREQUAL Xcode)
         PROPERTIES
         XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
         XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.totalcross.vm
    -    XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 10.0
    +    XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 12.1
         XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES YES
         XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${IOS_BUNDLE_SRCDIR}/tcvm/tcvm-Prefix.pch"
         XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES"
         XCODE_ATTRIBUTE_HEADER_SEARCH_PATHS "${INCLUDED_HEADERS_STR}"
    +    XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++0x"
    +    XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD c99
    +    STATIC_LIBRARY_FLAGS "${TCVM_XCODE_OTHER_LIBTOOLFLAGS}"
    +    XCODE_ATTRIBUTE_OTHER_LIBTOOLFLAGS "${TCVM_XCODE_OTHER_LIBTOOLFLAGS}"
    +    XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
       )
     endif()
     
    @@ -560,8 +707,6 @@ if(DEFINED SDL2_LIBRARY)
       target_link_libraries(tcvm ${SDL2_LIBRARY})
     endif(DEFINED SDL2_LIBRARY)
     
    -target_link_libraries(tcvm SQLite3)
    -
     if(DEFINED ANDROID_ABI)
       add_library(libskia SHARED IMPORTED)
       set_target_properties(# Specifies the target library.
    @@ -607,13 +752,17 @@ if(DEFINED ANDROID_ABI)
                             ${log-lib} 
                             ${jnigraphics-lib} )
     elseif(MSVC)
    -  if(${CMAKE_GENERATOR} STREQUAL "Visual Studio 9 2008 Pocket PC 2003 (ARMV4)")
    +  if(TARGET_WINCE)
         target_link_libraries(tcvm secchk.lib coredll.lib corelibc.lib ole32.lib oleaut32.lib uuid.lib commctrl.lib)
       else()
         target_link_libraries(tcvm winmm.lib ws2_32.lib imm32.lib Rasapi32.lib)
       endif()
     else()
    -  target_link_libraries(tcvm ${SKIA_LIBRARIES} pthread stdc++)
    +  if(CMAKE_GENERATOR STREQUAL Xcode)
    +    target_link_libraries(tcvm PRIVATE skia_static)
    +  else()
    +    target_link_libraries(tcvm ${SKIA_LIBRARIES} pthread stdc++)
    +  endif()
       if(NOT APPLE AND UNIX)
         target_link_libraries(tcvm fontconfig)
         if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm")
    @@ -649,29 +798,32 @@ add_executable(Launcher
       ${LAUNCHER_RESOURCES}
     )
     
    -if(MSVC AND ${CMAKE_GENERATOR} STREQUAL "Visual Studio 9 2008 Pocket PC 2003 (ARMV4)")
    +if(TARGET_WINCE)
       target_compile_options(Launcher PRIVATE /GS-)
       target_link_options(Launcher PRIVATE /STACK:0x200000 /ENTRY:)
     endif()
     
     if(MSVC)
       target_link_libraries(Launcher MSVCRT.lib)
    +  if(${CMAKE_GENERATOR} STREQUAL "Visual Studio 17 2022")
    +    target_link_libraries(Launcher vcruntime.lib libucrt.lib)
    +  endif()
     else()
       target_link_libraries(Launcher dl)
     endif(MSVC)
     endif()
     
    -if(MSVC)
    -  INCLUDE_EXTERNAL_MSPROJECT(Litebase ${CMAKE_CURRENT_SOURCE_DIR}/../LitebaseSDK/builders/vc2008/Litebase.vcproj)
    -  if(${CMAKE_GENERATOR} STREQUAL "Visual Studio 9 2008 Pocket PC 2003 (ARMV4)")
    -    INCLUDE_EXTERNAL_MSPROJECT(Datalogic ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Datalogic.vcproj)
    -    INCLUDE_EXTERNAL_MSPROJECT(Dolphin ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Dolphin.vcproj)
    -    INCLUDE_EXTERNAL_MSPROJECT(Intermec ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Intermec.vcproj)
    -    INCLUDE_EXTERNAL_MSPROJECT(Motorola ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Motorola.vcproj)
    -    INCLUDE_EXTERNAL_MSPROJECT(OpticonH16 ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/OpticonH16.vcproj)
    -    add_dependencies(OpticonH16 tcvm)
    -    add_dependencies(Dolphin tcvm)
    -    add_dependencies(Intermec tcvm)
    -    INCLUDE_EXTERNAL_MSPROJECT(Pidion ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Pidion.vcproj)
    -  endif()
    -endif()
    +# Disabled for the time being
    +# if(MSVC)
    +#   if(TARGET_WINCE)
    +#     INCLUDE_EXTERNAL_MSPROJECT(Datalogic ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Datalogic.vcproj)
    +#     INCLUDE_EXTERNAL_MSPROJECT(Dolphin ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Dolphin.vcproj)
    +#     INCLUDE_EXTERNAL_MSPROJECT(Intermec ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Intermec.vcproj)
    +#     INCLUDE_EXTERNAL_MSPROJECT(Motorola ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Motorola.vcproj)
    +#     INCLUDE_EXTERNAL_MSPROJECT(OpticonH16 ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/OpticonH16.vcproj)
    +#     add_dependencies(OpticonH16 tcvm)
    +#     add_dependencies(Dolphin tcvm)
    +#     add_dependencies(Intermec tcvm)
    +#     INCLUDE_EXTERNAL_MSPROJECT(Pidion ${CMAKE_CURRENT_SOURCE_DIR}/vc2008/Pidion.vcproj)
    +#   endif()
    +# endif()
    diff --git a/TotalCrossVM/android/.gitignore b/TotalCrossVM/android/.gitignore
    new file mode 100644
    index 0000000000..3e52d74516
    --- /dev/null
    +++ b/TotalCrossVM/android/.gitignore
    @@ -0,0 +1,57 @@
    +#built application files
    +*.apk
    +*.ap_
    +
    +# files for the dex VM
    +*.dex
    +
    +# Java class files
    +*.class
    +
    +# generated files
    +bin/
    +gen/
    +
    +# Built application files
    +/**/build/
    +/captures
    +obj/
    +.externalNativeBuild
    +.navigation
    +output.json 
    +
    +# Crashlytics configuations
    +com_crashlytics_export_strings.xml
    +
    +# Local configuration file (sdk path, etc)
    +local.properties
    +
    +# Gradle generated files
    +.gradle/
    +
    +# Signing files
    +.signing/
    +
    +# User-specific configurations
    +.idea/libraries/
    +.idea/workspace.xml
    +.idea/tasks.xml
    +.idea/.name
    +.idea/compiler.xml
    +.idea/copyright/profiles_settings.xml
    +.idea/encodings.xml
    +.idea/misc.xml
    +.idea/modules.xml
    +.idea/scopes/scope_settings.xml
    +.idea/vcs.xml
    +.idea/caches/
    +*.iml
    +
    +# OS-specific files
    +.DS_Store
    +.DS_Store?
    +._*
    +.Spotlight-V100
    +.Trashes
    +ehthumbs.db
    +Thumbs.db
    diff --git a/TotalCrossVM/android/app/.gitignore b/TotalCrossVM/android/app/.gitignore
    new file mode 100644
    index 0000000000..796b96d1c4
    --- /dev/null
    +++ b/TotalCrossVM/android/app/.gitignore
    @@ -0,0 +1 @@
    +/build
    diff --git a/TotalCrossVM/android/app/build.gradle b/TotalCrossVM/android/app/build.gradle
    index 629f9bd4f3..3bd01217a6 100644
    --- a/TotalCrossVM/android/app/build.gradle
    +++ b/TotalCrossVM/android/app/build.gradle
    @@ -1,51 +1,61 @@
     apply plugin: 'com.android.application'
    +apply plugin: 'kotlin-android'
     
    - repositories { 
    -     jcenter()
    + repositories {
    +     google()
    +     mavenCentral()
          maven {
              url "http://maven.totalcross.com/artifactory/build"
    +         allowInsecureProtocol true
              credentials {
                  username = "$System.env.TC_REPO_USERNAME"
                  password = "$System.env.TC_REPO_PASSWORD"
              }
    +         content {
    +             includeGroupByRegex "totalcross(\\..*)?"
    +             includeGroupByRegex "com\\.totalcross(\\..*)?"
    +          }
    +         // allows downloading artifacts without metadata (a pom file)
    +         metadataSources { artifact() }
          }
      }
     
     def buildType // stores build type, debug or release
     
     android {
    -    compileSdkVersion 29
    -    ndkVersion "21.0.6113669"
    -    compileOptions {
    -    	// compile with Java 8 to allow using try-with-resources without raising the API level to 19
    -        sourceCompatibility JavaVersion.VERSION_1_8
    -        targetCompatibility JavaVersion.VERSION_1_8
    -    }
    +    namespace 'totalcross.android'
    +    compileSdk 35
     
    -    aaptOptions {
    +    androidResources {
             noCompress 'zip'
         }
     
    +    sourceSets {
    +        main {
    +            java {
    +                exclude 'totalcross/android/scanners/IntermecScanner.java'
    +            }
    +        }
    +    }
    +
         flavorDimensions "deploy"
         productFlavors {
             standard { dimension "deploy" }
    -        singleApk { dimension "deploy" }
    -        includeSms { dimension "deploy" }
         }
     
         defaultConfig {
             applicationId "totalcross.android"
    -        minSdkVersion 15
    -        targetSdkVersion 29
    +        minSdkVersion 23
    +        targetSdkVersion 35
             versionCode 305419896
             versionName "!1.0!"
             multiDexEnabled true
    -        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    +        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
         }
     
         buildTypes {
             release {
    -            minifyEnabled false
    +            minifyEnabled true
                 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
             }
             debug {}
    @@ -58,108 +68,80 @@ android {
                 buildType = 'debug'
             }
         }
    -    
    -    task assembleAssets(type:Zip) {
    -        // outputs.upToDateWhen { false }
    -        from ('${rootDir}/../../TotalCrossSDK/dist/vm') {
    +
    +    task assembleAssets(type: Zip) {
    +        archiveBaseName = "tcfiles"
    +        entryCompression ZipEntryCompression.STORED
    +        destinationDirectory = file('src/main/assets')
    +
    +        from("${rootDir}/../../TotalCrossSDK/dist/vm") {
                 include 'TCBase.tcz'
                 include 'TCUI.tcz'
             }
    -        from ('${rootDir}/../../TotalCrossSDK/etc/fonts') {
    +        from("${rootDir}/../../TotalCrossSDK/etc/fonts") {
                 include 'TCFont.tcz'
             }
    -        from ('${rootDir}/../../LitebaseSDK/dist/lib/') {
    -            include 'LitebaseLib.tcz'
    -        }
    -        entryCompression ZipEntryCompression.STORED
    -        baseName 'tcfiles'
    -        destinationDir file('src/standard/assets') // directory that you want your archive to be placed in
         }
     
    -	assemble.mustRunAfter assembleAssets
    -    project.afterEvaluate{
    -	    applicationVariants.all { com.android.build.gradle.api.ApplicationVariant variant ->
    -		    	variant.assemble.dependsOn(['assembleAssets'])
    -		}
    -        android.applicationVariants.all { variant ->
    -            variant.assemble.doLast {
    -                if (variant.flavorName == 'standard') {
    -                    variant.outputs.each { output ->
    -                        def outputFile = output.outputFile
    -                        if (outputFile != null && outputFile.name.endsWith('.apk')) {
    -                            copy {
    -                                from outputFile
    -                                into "${rootDir}/../../TotalCrossSDK/dist/vm/android"
    -                                rename { 'TotalCross.apk' }
    -                            }
    -                            copy {
    -                                from fileTree('build/intermediates/processed_res/' + variant.name +'/out').files
    -                                into "${rootDir}/../../TotalCrossSDK/etc/launchers/android"
    -                                include 'resources-' + variant.name + '.ap_'
    -                                rename { 'resources.ap_' }
    -                            }
    -                        }
    -                    }
    -                } else {
    -                    variant.outputs.each { output ->
    -                        def outputFile = output.outputFile
    -                        if (outputFile != null && outputFile.name.endsWith('.apk')) {
    -                            copy {
    -                                from zipTree(outputFile).files
    -                                into "${rootDir}/../../TotalCrossSDK/etc/tools/android"
    -                                include 'AndroidManifest.xml', 'resources.arsc'
    -                                rename { String fileName ->
    -                                    fileName.replace('.', '_' + variant.flavorName + '.')
    -                                }
    -                            }
    -                        }
    -                    }
    -                }
    -            }
    -        }
    -	}
    +    afterEvaluate {
    +        tasks.named("preBuild").get() dependsOn("assembleAssets")
    +    }
    +
    +    // project.afterEvaluate {
    +    //     bundleRelease.doLast {
    +    //         copy {
    +    //             from("${buildDir}/outputs/bundle/standardRelease") {
    +    //                 include '*-release.aab'
    +    //                 into "${rootDir}/../../TotalCrossSDK/dist/vm/android"
    +    //                 rename { 'TotalCross.aab' }
    +    //             }
    +    //         }
    +    //     }
    +	// }
     }
     
     dependencies {
         implementation project(":tcvm")
         implementation fileTree(dir: 'libs', include: ['*.jar'])
     
    -    implementation 'com.android.support:appcompat-v7:26.0.2'
    -
    -    implementation 'com.google.android.gms:play-services-ads:' + googleGmsVersion
    -    implementation 'com.google.android.gms:play-services-maps:' + googleGmsVersion
    -    implementation 'com.google.android.gms:play-services-location:' + googleGmsVersion
    -    implementation 'com.google.firebase:firebase-core:' + googleGmsVersion
    -    implementation 'com.google.firebase:firebase-messaging:' + googleGmsVersion
    +    implementation "com.google.guava:guava:31.0.1-android"
     
    -    implementation 'com.github.cliftonlabs:json-simple:2.1.2'
    +    def cameraxVersion = "1.5.2"
    +    implementation "androidx.camera:camera-core:${cameraxVersion}"
    +    implementation "androidx.camera:camera-camera2:${cameraxVersion}"
    +    implementation "androidx.camera:camera-lifecycle:${cameraxVersion}"
    +    // CameraX View class
    +    implementation "androidx.camera:camera-view:1.0.0-alpha25"
     
    -    // Supports Android 4.0.3 and later (API level 15)
    -    implementation 'com.journeyapps:zxing-android-embedded:2.3.0@aar'
    +    implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' // dependency for FirebaseUtils, but only required when maps is 17+, go figure...
    +    implementation 'com.google.android.gms:play-services-maps:18.2.0'
    +    implementation 'com.google.android.gms:play-services-location:21.1.0'
    +    implementation 'com.google.firebase:firebase-core:15.0.2'
    +    implementation 'com.google.firebase:firebase-messaging:15.0.2'
     
    -    // Supports Android 2.1 and later (API level 7), but not optimal for later Android versions.
    -    // If you only plan on supporting Android 4.0.3 and up, you don't need to include this.
    -    implementation 'com.journeyapps:zxing-android-legacy:2.3.0@aar'
    +    implementation 'com.github.cliftonlabs:json-simple:2.1.2'
     
    -    // Convenience library to launch the scanning Activities.
    -    // It automatically picks the best scanning library from the above two, depending on the
    -    // Android version and what is available.
    -    implementation 'com.journeyapps:zxing-android-integration:2.3.0@aar'
    +    // API level 19+ combined with zxing:core 3.3.0
    +    implementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false }
     
         // Version 3.0.x of zxing core contains some code that is not compatible on Android 2.2 and earlier.
         // This mostly affects encoding, but you should test if you plan to support these versions.
         // Older versions e.g. 2.2 may also work if you need support for older Android versions.
    -    implementation 'com.google.zxing:core:3.2.0'
    +    implementation 'com.google.zxing:core:3.3.0'
     
         // Enables MultiDex. This allows the app to have more than 64k methods
    -    implementation 'com.android.support:multidex:1.0.1'
    +    implementation 'androidx.multidex:multidex:2.0.0'
     
         testImplementation 'junit:junit:4.12'
         androidTestImplementation 'com.android.support.test:runner:1.0.2'
         androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    -    
    -    implementation 'com.android.support:customtabs:25.1.0'
    +   
    +    implementation 'androidx.browser:browser:1.0.0'
     
         //Youtube API
         implementation files('libs/YouTubeAndroidPlayerApi.jar')
    -}
    \ No newline at end of file
    +
    +    implementation("androidx.appcompat:appcompat:1.4.2")
    +    implementation "androidx.core:core-ktx:$kotlin_version"
    +    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    +}
    diff --git a/TotalCrossVM/android/app/libs/DataCollection.jar b/TotalCrossVM/android/app/libs/DataCollection.jar
    new file mode 100644
    index 0000000000..1921377cfd
    Binary files /dev/null and b/TotalCrossVM/android/app/libs/DataCollection.jar differ
    diff --git a/TotalCrossVM/android/app/libs/DataCollectionHoneywell.jar b/TotalCrossVM/android/app/libs/DataCollectionHoneywell.jar
    new file mode 100644
    index 0000000000..272fac4bb6
    Binary files /dev/null and b/TotalCrossVM/android/app/libs/DataCollectionHoneywell.jar differ
    diff --git a/TotalCrossVM/android/app/libs/com.symbol.emdk-6.7.jar b/TotalCrossVM/android/app/libs/com.symbol.emdk-6.7.jar
    new file mode 100644
    index 0000000000..a45b981ebe
    Binary files /dev/null and b/TotalCrossVM/android/app/libs/com.symbol.emdk-6.7.jar differ
    diff --git a/TotalCrossVM/android/app/proguard-rules.pro b/TotalCrossVM/android/app/proguard-rules.pro
    index f1b424510d..737a89b034 100644
    --- a/TotalCrossVM/android/app/proguard-rules.pro
    +++ b/TotalCrossVM/android/app/proguard-rules.pro
    @@ -19,3 +19,9 @@
     # If you keep the line number information, uncomment this to
     # hide the original source file name.
     #-renamesourcefileattribute SourceFile
    +
    +# keep classes on "totalcross" package
    +-keep class totalcross.** {
    +	;
    +    ;
    +}
    diff --git a/TotalCrossVM/android/app/src/includeSms/AndroidManifest.xml b/TotalCrossVM/android/app/src/includeSms/AndroidManifest.xml
    deleted file mode 100644
    index be97ef2a6a..0000000000
    --- a/TotalCrossVM/android/app/src/includeSms/AndroidManifest.xml
    +++ /dev/null
    @@ -1,28 +0,0 @@
    -
    -
    -
    -	
    -		
    -			
    -				
    -				
    -			
    -		
    -
    -		
    -			
    -				
    -			
    -		
    -
    -		
    -		
    -	
    -	
    -    
    -    
    -    
    -
    diff --git a/TotalCrossVM/android/app/src/includeSms/java/totalcross/app/stub/StartupIntentReceiver.java b/TotalCrossVM/android/app/src/includeSms/java/totalcross/app/stub/StartupIntentReceiver.java
    deleted file mode 100644
    index 82b7ad9850..0000000000
    --- a/TotalCrossVM/android/app/src/includeSms/java/totalcross/app/stub/StartupIntentReceiver.java
    +++ /dev/null
    @@ -1,23 +0,0 @@
    -package totalcross.app.stub;
    -
    -import android.content.*;
    -
    -public class StartupIntentReceiver extends BroadcastReceiver 
    -{
    -   public static int call = 123454321; // will be changed by Deployer4A
    -   
    -   public void onReceive(Context context, Intent intent) 
    -   {
    -      int no = 123454320;
    -      if (true) no++; // prevent compiler optimization
    -      if (call != no)
    -      {
    -         // Create intent which will finally start the Main-Activity.
    -         Intent myStarterIntent = new Intent(context, Stub.class);
    -         // Set the Launch-Flag to the Intent.
    -         myStarterIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    -         // Send the Intent to the OS.
    -         context.startActivity(myStarterIntent);
    -      }
    -   }
    -}
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/includeSms/java/totalcross/app/stub/Stub.java b/TotalCrossVM/android/app/src/includeSms/java/totalcross/app/stub/Stub.java
    deleted file mode 100644
    index a0bf2aa276..0000000000
    --- a/TotalCrossVM/android/app/src/includeSms/java/totalcross/app/stub/Stub.java
    +++ /dev/null
    @@ -1,123 +0,0 @@
    -// Copyright (C) 2000-2013 SuperWaba Ltda.
    -// Copyright (C) 2014-2020 TotalCross Global Mobile Platform Ltda.
    -//
    -// SPDX-License-Identifier: LGPL-2.1-only
    -
    -
    -
    -package totalcross.app.stub;
    -
    -import totalcross.*;
    -
    -import android.app.*;
    -import android.app.ActivityManager.*;
    -import android.content.*;
    -import android.content.pm.*;
    -import android.os.*;
    -import java.util.*;
    -
    -public class Stub extends Activity 
    -{
    -   private class LengthyTask extends AndroidUtils.StartupTask 
    -   {
    -       public LengthyTask(Activity activity) {
    -           super(activity);
    -       }
    -       
    -      protected void onPostExecute(Integer result) 
    -      {
    -         try
    -         {
    -            runApplication();
    -         }
    -         catch (Exception e)
    -         {
    -            AndroidUtils.handleException(e,true);
    -         }
    -      }
    -   }
    -   /** Called when the activity is first created. */   
    -   public void onCreate(Bundle savedInstanceState) 
    -   {
    -      super.onCreate(savedInstanceState);
    -      
    -      try
    -      {
    -         AndroidUtils.initialize(this);
    -         new LengthyTask(this).execute((Object)null);
    -      }
    -      catch (Exception e)
    -      {
    -         AndroidUtils.handleException(e,true);
    -      }
    -   }
    -   
    -   private void runApplication() throws Exception
    -   {
    -      // launch the vm's intent
    -      Intent intent = new Intent("android.intent.action.MAIN");
    -      intent.setClassName("totalcross.android","totalcross.android.Loader");
    -      
    -      Hashtable ht = AndroidUtils.readVMParameters();
    -      String lastTCZ = ht.get("tczname");
    -      ht.clear();
    -      // set some parameters
    -      String app = getClass().getName();
    -      int dot = app.lastIndexOf('.');
    -      String tczName = app.substring(dot+1); // strip the package
    -      ht.put("package", app.substring(0,dot));
    -      ht.put("tczname", tczName);
    -      ht.put("apppath", AndroidUtils.pinfo.applicationInfo.dataDir);
    -      ht.put("fullscreen", "fullscreen:1".equals(getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA).metaData.getString("isFullScreen")) ? "true" : "false"); // no longer used
    -      // set commandline
    -      Bundle extras = getIntent().getExtras();
    -      if (extras != null && extras.containsKey("cmdline"))
    -         ht.put("cmdline",extras.getString("cmdline"));
    -
    -      int flags = Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_FROM_BACKGROUND; // clear_top is essential to make it work (otherwise, the last called app will be launched instead of the current one)
    -      boolean sameProgram = lastTCZ != null && tczName.equals(lastTCZ);
    -      AndroidUtils.debug(sameProgram ? "Program "+tczName+" being resumed" : "Program change detected: "+lastTCZ+" -> "+tczName);
    -      if (!sameProgram) // if its not the same app that was launched, remove all other tasks
    -         flags |= Intent.FLAG_ACTIVITY_CLEAR_TOP;
    -      intent.setFlags(flags);
    -      AndroidUtils.writeVMParameters(ht);
    -      
    -      try
    -      {
    -         startActivity(intent);
    -         
    -         ActivityManager actvityManager = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
    -         int imp = getTCVMImportance(actvityManager, !sameProgram);
    -         if (imp == RunningAppProcessInfo.IMPORTANCE_VISIBLE || (!sameProgram && imp == RunningAppProcessInfo.IMPORTANCE_BACKGROUND)) // guich@tc123b: if the vm was already running...
    -         {
    -            while (true) // wait the old process die
    -            {
    -               imp = getTCVMImportance(actvityManager,false);
    -               if (imp != 0)
    -                  try {Thread.sleep(100);} catch (Exception e) {}
    -               else 
    -                  break;
    -            } 
    -            startActivity(intent); // and call it again
    -         }
    -      }         
    -      catch (android.content.ActivityNotFoundException anfe)
    -      {
    -         AndroidUtils.error("This program requires the TotalCross Virtual Machine to run. Please contact your software's vendor or download the DEMO version from\nwww.totalcross.com/install\nwhich will run during 80 hours.",true);
    -      }
    -      System.exit(99);
    -   }
    -   
    -   private int getTCVMImportance(ActivityManager actvityManager, boolean kill)
    -   {
    -      List list = actvityManager.getRunningAppProcesses();
    -      int n = list.size();
    -      for(int j = 0; j < n; j++)
    -      {
    -         RunningAppProcessInfo info = list.get(j);
    -         if (info.processName.equals("totalcross.android"))
    -            return info.importance;
    -      }
    -      return 0;
    -   }
    -}
    diff --git a/TotalCrossVM/android/app/src/main/AndroidManifest.xml b/TotalCrossVM/android/app/src/main/AndroidManifest.xml
    index 54b48b7157..54cc83a401 100644
    --- a/TotalCrossVM/android/app/src/main/AndroidManifest.xml
    +++ b/TotalCrossVM/android/app/src/main/AndroidManifest.xml
    @@ -1,14 +1,13 @@
     
     
    -    
    +
         
    +            android:exported="true">
    +            
    +                
    +                
    +            
    +        
             
             
             
    -        
    +        
    +        
    +        
    +        
             
             
             
    -        
    -         
    -           
    -           
    -         
    -         
    -           
    -           
    -                  
    -        
    +        
    +        
             
             
    +
    +        
    +            
    +                
    +            
    +        
    +
    +        
    +
             
             
     		
    +			android:name="totalcross.android.firebase.FirebaseMessageReceiver"
    +            android:exported="true">
     			
     				
     			
     		
             
             
    +            android:name="totalcross.android.firebase.MyInstanceIDListenerService"
    +            android:exported="true">
                 
                     
                 
             
             
    @@ -75,6 +96,27 @@
                     android:resource="@xml/provider_paths"/>
             
         
    +
    +    
    +    
    +    
    +
    +    
    +    
    +
    +    
    +    
    +
    +    
    +    
    +
     	
    @@ -94,8 +136,11 @@
         
         
         
    +    
         
         
    +    
    +    
         
         
         
    @@ -108,8 +153,6 @@
         
          -->
         
    -    
    -    
         
         
         
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/AndroidUtils.java b/TotalCrossVM/android/app/src/main/java/totalcross/AndroidUtils.java
    index 2dda3063f0..1359f75d66 100644
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/AndroidUtils.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/AndroidUtils.java
    @@ -11,6 +11,8 @@
     import java.io.*;
     import java.util.*;
     import java.util.zip.*;
    +import java.nio.ByteOrder;
    +
     import android.app.*;
     import android.content.*;
     import android.content.pm.*;
    @@ -158,6 +160,12 @@ private static String getRealPath(String path)
        }
     
         private static void loadTCVM(Context context) {
    +        try {
    +           // Correct way to load a native library, required for aab distribution
    +           System.loadLibrary("tcvm");
    +        } catch (Throwable e0) {
    +            handleException(e0, false);
    +        
             try {
                 System.load(getRealPath(context.getApplicationInfo().nativeLibraryDir) + "/libtcvm.so");
             } catch (Throwable e1) {
    @@ -185,6 +193,7 @@ private static void loadTCVM(Context context) {
                     }
                 }
             }
    +        }
         }
     
        public static void checkInstall(Context context) throws Exception
    @@ -410,4 +419,32 @@ public static void copyStreamToFile(InputStream inputStream, String out) throws
              fout.write(buf,0,r);
           fout.close();
        }
    +
    +   private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
    +   private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
    +           
    +   public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {
    +   
    +       // our output size will be exactly 2x byte-array length
    +       final char[] buffer = new char[byteArray.length * 2];
    +   
    +       // choose lower or uppercase lookup table
    +       final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;
    +   
    +       int index;
    +       for (int i = 0; i < byteArray.length; i++) {
    +           // for little endian we count from last to first
    +           index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;
    +           
    +           // extract the upper 4 bit and look up char (0-A)
    +           buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
    +           // extract the lower 4 bit and look up char (0-A)
    +           buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    +       }
    +       return new String(buffer);
    +   }
    +   
    +   public static String encode(byte[] byteArray) {
    +       return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
    +   }
     }
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/Launcher4A.java b/TotalCrossVM/android/app/src/main/java/totalcross/Launcher4A.java
    index fdc35a2d45..ea21205d37 100755
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/Launcher4A.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/Launcher4A.java
    @@ -7,42 +7,95 @@
     
     package totalcross;
     
    -import totalcross.android.*;
    -import totalcross.android.Loader;
    -
    -import android.app.*;
    -import android.app.ActivityManager.*;
    -import android.content.*;
    -import android.content.pm.*;
    -import android.content.res.*;
    -import android.graphics.*;
    +import android.Manifest;
    +import android.app.Activity;
    +import android.app.ActivityManager;
    +import android.app.ActivityManager.MemoryInfo;
    +import android.app.AlertDialog;
    +import android.content.Context;
    +import android.content.DialogInterface;
    +import android.content.Intent;
    +import android.content.IntentFilter;
    +import android.content.pm.PackageInfo;
    +import android.content.pm.PackageManager;
    +import android.content.res.Configuration;
    +import android.graphics.ImageFormat;
    +import android.graphics.PixelFormat;
    +import android.graphics.Rect;
     import android.hardware.Camera;
    -import android.location.*;
    -import android.media.*;
    -import android.net.*;
    -import android.os.*;
    +import android.hardware.camera2.CameraCharacteristics;
    +import android.hardware.camera2.CameraManager;
    +import android.hardware.camera2.params.StreamConfigurationMap;
    +import android.location.Location;
    +import android.location.LocationManager;
    +import android.media.AudioManager;
    +import android.media.MediaRecorder;
    +import android.media.SoundPool;
    +import android.media.ToneGenerator;
    +import android.net.Uri;
    +import android.os.Build;
    +import android.os.Bundle;
    +import android.os.Environment;
    +import android.os.Handler;
     import android.os.Looper;
    -import android.provider.*;
    -import android.telephony.*;
    -import android.telephony.gsm.*;
    -import android.text.*;
    -import android.text.method.KeyListener;
    -import android.util.*;
    -import android.view.*;
    -import android.view.View.*;
    -import android.view.inputmethod.*;
    -import android.widget.*;
    -import java.io.*;
    -import java.lang.reflect.*;
    +import android.os.Message;
    +import android.os.ResultReceiver;
    +import android.os.StatFs;
    +import android.os.Vibrator;
    +import android.provider.Settings;
    +import android.telephony.CellLocation;
    +import android.telephony.PhoneStateListener;
    +import android.telephony.TelephonyManager;
    +import android.telephony.gsm.GsmCellLocation;
    +import android.text.InputType;
    +import android.util.DisplayMetrics;
    +import android.util.Size;
    +import android.view.KeyEvent;
    +import android.view.MotionEvent;
    +import android.view.ScaleGestureDetector;
    +import android.view.Surface;
    +import android.view.SurfaceHolder;
    +import android.view.SurfaceView;
    +import android.view.View;
    +import android.view.View.OnKeyListener;
    +import android.view.inputmethod.BaseInputConnection;
    +import android.view.inputmethod.EditorInfo;
    +import android.view.inputmethod.InputConnection;
    +import android.view.inputmethod.InputMethodManager;
    +import android.widget.TextView;
    +
    +import androidx.annotation.Nullable;
    +import androidx.core.app.ActivityCompat;
    +import androidx.core.content.ContextCompat;
    +import androidx.core.graphics.Insets;
    +
    +import org.jetbrains.annotations.Contract;
    +
    +import java.io.DataOutputStream;
    +import java.io.File;
    +import java.io.FileInputStream;
    +import java.io.FileNotFoundException;
    +import java.io.FileOutputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.RandomAccessFile;
    +import java.lang.reflect.Method;
     import java.math.BigDecimal;
     import java.math.RoundingMode;
    -import java.net.*;
    -import java.util.*;
    -import java.util.zip.*;
    -
    -import android.support.v4.content.ContextCompat;
    -import android.support.v4.app.ActivityCompat;
    -import android.Manifest;
    +import java.net.HttpURLConnection;
    +import java.net.URL;
    +import java.util.ArrayList;
    +import java.util.HashMap;
    +import java.util.Hashtable;
    +import java.util.List;
    +import java.util.Locale;
    +import java.util.zip.ZipEntry;
    +import java.util.zip.ZipInputStream;
    +import java.util.zip.ZipOutputStream;
    +
    +import totalcross.android.GPSHelper;
    +import totalcross.android.Loader;
    +import totalcross.android.Scanner4A;
     
     final public class Launcher4A extends SurfaceView implements SurfaceHolder.Callback, MainClass, OnKeyListener
     {
    @@ -227,6 +280,21 @@ private int getOrientation()
        }
        
        private android.view.Surface lastSurface;
    +
    +   private void tryBootstrapSurface(SurfaceHolder holder)
    +   {
    +      android.view.Surface surface = holder == null ? null : holder.getSurface();
    +      Rect rect = holder == null ? null : holder.getSurfaceFrame();
    +      int w = rect == null ? getWidth() : rect.width();
    +      int h = rect == null ? getHeight() : rect.height();
    +      if (surface != null && surface.isValid() && w > 0 && h > 0 && eventThread != null)
    +      {
    +         lastSurface = surface;
    +         lastScreenW = w;
    +         lastScreenH = h;
    +         sendScreenChangeEvent();
    +      }
    +   }
        
        public void surfaceChanged(final SurfaceHolder holder, int format, int w, int h) 
        {
    @@ -243,7 +311,7 @@ public void surfaceChanged(final SurfaceHolder holder, int format, int w, int h)
              appTitleH = lastScreenH - h; 
           
           if (sipVisible && !rotated) // sip changed and no rotation occured?
    -         instance.nativeInitSize(null,-999,h); // signal vm that the keyboard will appear
    +         instance.nativeInitSize(null,-999,100); // signal vm that the keyboard will appear at 100%
           else
           {
              instance.nativeInitSize(null,-999,0); // signal vm that the keyboard will hide
    @@ -332,11 +400,26 @@ public void surfaceCreated(SurfaceHolder holder)
              eventThread = new TCEventThread(this);
              eventThread.popTime = 20;
              sgd = new ScaleGestureDetector(loader, scaleList = new ScaleListener());
    +         tryBootstrapSurface(holder);
    +         post(new Runnable()
    +         {
    +            public void run()
    +            {
    +               tryBootstrapSurface(getHolder());
    +            }
    +         });
           }
           else
           {
    -         lastSurface = holder.getSurface();
    -         sendScreenChangeEvent();
    +         tryBootstrapSurface(holder);
    +         if ((lastSurface == null || !lastSurface.isValid() || lastScreenW <= 0 || lastScreenH <= 0))
    +            post(new Runnable()
    +            {
    +               public void run()
    +               {
    +                  tryBootstrapSurface(getHolder());
    +               }
    +            });
           }
        }
     
    @@ -441,6 +524,8 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh)
           super.onSizeChanged(w, h, oldw, oldh);
        }
     
    +   private boolean backDownReceived = false;
    +   
        public boolean onKey(View v, int keyCode, KeyEvent event)
        {
           if (Scanner4A.scanner != null && Scanner4A.scanner.checkScanner(event))
    @@ -448,12 +533,23 @@ public boolean onKey(View v, int keyCode, KeyEvent event)
     
           if (keyCode == KeyEvent.KEYCODE_BACK) // guich@tc130: if the user pressed the back key on the SIP, don't pass it to the application
           {
    -         if (!hardwareKeyboardIsVisible && sipVisible)
    -         {
    -            if (event.getAction() == KeyEvent.ACTION_UP)
    -               setSIP(SIP_HIDE,false);
    -            return false;
    -         }
    +          if (!hardwareKeyboardIsVisible && (sipVisible || sipWasOpen)) {
    +              if (event.getAction() == KeyEvent.ACTION_DOWN) {
    +                  backDownReceived = true;
    +              }
    +              /*
    +               * Only react to the first ACTION_UP by checking backDownReceived.
    +               * Uses backDownReceived and sipWasOpen to correctly handle the keyboard
    +               * on Samsung's OneUI, because it reports the keyboard as closed before
    +               * the back event is processed, and sends KeyEvent.ACTION_UP twice.
    +               */
    +              if (backDownReceived && event.getAction() == KeyEvent.ACTION_UP) {
    +                  setSIP(SIP_HIDE, false);
    +                  backDownReceived = false;
    +                  Launcher4A.sipWasOpen = false;
    +              }
    +              return false;
    +          }
           }
           switch (event.getAction())
           {
    @@ -604,16 +700,28 @@ public static void stopVM()
        
        public static boolean eventIsAvailable()
        {
    -      if (appPaused)
    -         try {Thread.sleep(250);} catch (Exception e) {}
    +      if (appPaused) {
    +         try {
    +            Thread.sleep(250);
    +         } catch (Exception e) {
    +            // just ignore
    +         } 
    +         return false;
    +      }
           return eventThread.eventAvailable();
        }
        
        public static void pumpEvents()
        {
    -      if (appPaused)
    -         try {Thread.sleep(250);} catch (Exception e) {}
    -      eventThread.pumpEvents();
    +      if (appPaused) {
    +         try {
    +            Thread.sleep(250);
    +         } catch (Exception e) {
    +            // just ignore
    +         }
    +      } else {
    +         eventThread.pumpEvents();
    +      }
        }
        
        public static void alert(String msg) // if called from android package classes, must pass FALSE
    @@ -659,7 +767,7 @@ static void setDeviceTitle(String title)
           loader.achandler.sendMessage(msg);
        }
        
    -   static void showCamera(String fileName, int stillQuality, int width, int height, boolean allowRotation, int cameraType)
    +   static void showCamera(String fileName, int stillQuality, int width, int height, boolean allowRotation, int cameraType, int videoTimeLimit, int targetFps, int bitrate)
        {
           Message msg = loader.achandler.obtainMessage();
           Bundle b = new Bundle();
    @@ -669,6 +777,9 @@ static void showCamera(String fileName, int stillQuality, int width, int height,
           b.putInt("showCamera.height",height);
           b.putBoolean("showCamera.allowRotation", allowRotation);
           b.putInt("showCamera.cameraType", cameraType);
    +      b.putInt("showCamera.videoTimeLimit", videoTimeLimit);
    +      b.putInt("showCamera.targetFps", targetFps);
    +      b.putInt("showCamera.bitrate", bitrate);
           b.putInt("type",Loader.CAMERA);
           msg.setData(b);
           loader.achandler.sendMessage(msg);
    @@ -698,7 +809,9 @@ public void _postEvent(int type, int key, int x, int y, int modifiers, int timeS
        public static final int SIP_BOTTOM = 10002;
        public static final int SIP_SHOW = 10003;
        
    -   static boolean sipVisible,wasNumeric;
    +   public static boolean sipVisible;
    +   public static boolean sipWasOpen = false;
    +   static boolean wasNumeric;
        
        class SipClosedReceiver extends ResultReceiver
        {
    @@ -744,8 +857,8 @@ public static void setSIP(int sipOption, boolean numeric)
           {
              case SIP_HIDE:
                 sipVisible = false;
    -            if (Loader.adView != null)
    -               showAds(true);
    +            // if (Loader.adView != null)
    +            //    showAds(true);
                 if (Loader.isFullScreen)
                    setLoaderFullScreen(true);
                 else
    @@ -757,8 +870,8 @@ public static void setSIP(int sipOption, boolean numeric)
                 wasNumeric = numeric;
                 sipVisible = true;
                 imm.restartInput(instance);
    -            if (Loader.adView != null)
    -               showAds(false);
    +            // if (Loader.adView != null)
    +            //    showAds(false);
                 if (Loader.isFullScreen)
                    setLoaderFullScreen(false);
                 else
    @@ -783,11 +896,38 @@ public static void sendCloseSIPEvent()
              eventThread.pushEvent(SIP_CLOSED,0,0,0,0,0);
        }
     
    +   public int sipInsetBottom = -1;
    +
        public static int getAppHeight()
        {
    -      return instance.getHeight();
    +       if (instance.safeInsets == null) {
    +           return instance.getHeight();
    +       }
    +       return instance.getHeight() - (instance.sipInsetBottom > instance.safeInsets.bottom ? instance.sipInsetBottom : 0);
        }
        
    +    private Insets safeInsets;
    +
    +    public void onSafeAreaChanged(Insets insets) {
    +        safeInsets = insets;
    +    }
    +
    +    @Nullable
    +    @Contract(pure = true)
    +    static int[] getSafeAreaInsets()
    +    {
    +        if (instance.safeInsets == null) {
    +            return null;
    +        }
    +        int[] insets = new int[4];
    +        insets[0] = instance.safeInsets.top;
    +        insets[1] = instance.safeInsets.left;
    +        insets[2] = instance.safeInsets.bottom;
    +        insets[3] = instance.safeInsets.right;
    +
    +        return insets;
    +    }
    +
        public static int setElapsed(int n)
        {
           if (n == 0)
    @@ -821,6 +961,9 @@ public static void dial(String number)
        private static final int VIBRATE = 12;
        private static final int CLIPBOARD = 13;
        public static final int FIREBASE_MSG_RCVD = 14;
    +
    +   private static final int GPSFUNC_RESETDATA = 100;
    +   private static final int GPSFUNC_DOWNLOADDATA = 101;
        
        private static int oldBrightness;
        private static Vibrator vibrator;
    @@ -1051,6 +1194,10 @@ public static String gpsFunc(int what, int opc)
                 return GPSHelper.instance.gpsTurn(true);
              case GPSFUNC_STOP:
                 return GPSHelper.instance.gpsTurn(false);
    +         case GPSFUNC_RESETDATA:
    +            return GPSHelper.instance.gpsResetData();
    +         case GPSFUNC_DOWNLOADDATA:
    +            return GPSHelper.instance.gpsDownloadData();
           }
           return null;
        }
    @@ -1255,6 +1402,15 @@ public static int fileGetFreeSpace(String fileName)
           return (int)size;
        }
     
    +   public static String fileGetAppSpecificDir(String type, String dirName) {
    +      File externalFilesDir = instance.getContext().getExternalFilesDir(type);
    +      File file = new File(externalFilesDir, dirName);
    +      if (!file.mkdirs()) {
    +         AndroidUtils.debug(">>> Directory not created");
    +      }
    +      return file.getAbsolutePath();
    +   }
    +
        private static String paste;
        private static boolean pasted;
        
    @@ -1292,32 +1448,102 @@ public static void appPaused()
        
        public static void appResumed()
        {
    -      appPaused = false;
    +      SurfaceHolder holder = instance == null ? null : instance.getHolder();
    +      Surface surface = holder == null ? null : holder.getSurface();
           instance.nativeInitSize(null,-997,0); // signal vm to invalidate the textures
    +      if (eventThread != null && surface != null && surface.isValid() && lastScreenW > 0 && lastScreenH > 0)
    +      {
    +         instance.lastSurface = surface;
    +         instance.sendScreenChangeEvent();
    +      }
           if (eventThread != null)
              eventThread.pushEvent(APP_RESUMED, 0, 0, 0, 0, 0);
    +      appPaused = false;
        }
    -   
    -   public static String getNativeResolutions()
    -   {
    -      try
    -      {
    -         StringBuffer sb = new StringBuffer(32);
    -         Camera camera = Camera.open();
    -         Camera.Parameters parameters=camera.getParameters();
    -         List sizes = parameters.getSupportedPictureSizes();
    -         if (sizes == null)
    + 
    +   public static String getNativeResolutions() {
    +      try {
    +         CameraManager cameraManager = (CameraManager) instance.getContext().getSystemService(Context.CAMERA_SERVICE);
    +
    +         Size[] outputSizes = null;
    +         for (String cameraId : cameraManager.getCameraIdList()) {
    +            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
    +            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    +
    +            outputSizes = map.getOutputSizes(ImageFormat.JPEG);
    +            if (outputSizes != null && outputSizes.length > 0) {
    +               break;
    +            }
    +         }
    +
    +         if (outputSizes == null) {
                 return null;
    -         for (Camera.Size ss: sizes)
    -            sb.append(ss.width).append("x").append(ss.height).append(',');
    +         }
    +
    +         StringBuffer sb = new StringBuffer(32);
    +         for (Size size : outputSizes) {
    +            sb.append(size.getWidth()).append("x").append(size.getHeight()).append(',');
    +         }
    +
              int l = sb.length();
    -         if (l > 0)
    -            sb.setLength(l-1); // remove last ,
    -         camera.release();
    +         if (l > 0) {
    +            sb.setLength(l - 1); // remove last ,
    +         }
              return sb.toString();
    +      } catch (Exception e) {
    +         AndroidUtils.handleException(e, false);
    +         return null;
           }
    -      catch (Exception e)
    -      {
    +   }
    +
    +   public static String getNativeVideoResolutions() {
    +      try {
    +         StringBuilder sb = new StringBuilder(32);
    +
    +         CameraManager manager = (CameraManager) instance.getContext().getSystemService(Context.CAMERA_SERVICE);
    +         for (String cameraId : manager.getCameraIdList()) {
    +
    +            CameraCharacteristics characteristics =
    +                    manager.getCameraCharacteristics(cameraId);
    +
    +            Integer lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
    +
    +            if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
    +               continue;
    +            }
    +
    +            StreamConfigurationMap map =
    +                    characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    +
    +            if (map == null) {
    +               continue;
    +            }
    +
    +            // --- PREVIEW RESOLUTIONS ---
    +            // Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);
    +            // Log.d("RES", "Preview Sizes:");
    +            // for (Size s : previewSizes)
    +            //     Log.d("RES", "  " + s.getWidth() + "x" + s.getHeight());
    +
    +            // --- VIDEO RESOLUTIONS (MediaRecorder / MediaCodec) ---
    +            Size[] videoSizes = map.getOutputSizes(MediaRecorder.class);
    +            // Log.d("RES", "Video Sizes:");
    +            for (Size s : videoSizes) {
    +               sb.append(s.getWidth()).append("x").append(s.getHeight()).append(',');
    +            }
    +            int l = sb.length();
    +            if (l > 0) {
    +               sb.setLength(l-1); // remove last ,
    +            }
    +
    +            // --- IMAGE RESOLUTIONS ---
    +            // Size[] jpegSizes = map.getOutputSizes(ImageFormat.JPEG);
    +            // Log.d("RES", "JPEG Sizes:");
    +            // for (Size s : jpegSizes)
    +            //     Log.d("RES", "  " + s.getWidth() + "x" + s.getHeight());
    +         }
    +         return sb.toString();
    +      } catch (Exception e) {
              AndroidUtils.handleException(e,false);
              return null;
           }
    @@ -1776,13 +2002,20 @@ public static void enableSmsReceiver(boolean enabled, int port) {
         public static final PermissionHandler STORAGE = new PermissionHandler(
                 Loader.PermissionRequestCodes.EXTERNAL_STORAGE,
                 Manifest.permission.READ_EXTERNAL_STORAGE,
    -            Manifest.permission.WRITE_EXTERNAL_STORAGE);
    +            Manifest.permission.WRITE_EXTERNAL_STORAGE,
    +            Manifest.permission.MANAGE_EXTERNAL_STORAGE);
     
         public static final PermissionHandler CAMERA = new PermissionHandler(
                 Loader.PermissionRequestCodes.CAMERA,
                 Manifest.permission.CAMERA,
    -            Manifest.permission.READ_EXTERNAL_STORAGE,
    -            Manifest.permission.WRITE_EXTERNAL_STORAGE);
    +            Manifest.permission.RECORD_AUDIO,
    +            Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.READ_EXTERNAL_STORAGE,
    +            Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? Manifest.permission.READ_MEDIA_VIDEO : Manifest.permission.WRITE_EXTERNAL_STORAGE);
    +
    +    public static final PermissionHandler BLUETOOTH = new PermissionHandler(
    +            Loader.PermissionRequestCodes.BLUETOOTH,
    +            Manifest.permission.BLUETOOTH_SCAN,
    +            Manifest.permission.BLUETOOTH_CONNECT);
     
         public static class PermissionHandler {
     
    @@ -1853,8 +2086,33 @@ public void onRequestPermissionsResult(String[] permissions, int[] grantResults)
             }
         }
     
    +    private static Boolean needsManageExternalStorage = null;
    +
         public static int requestStoragePermission() {
    -        return STORAGE.requestPermissions();
    +       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    +          if (needsManageExternalStorage == null) {
    +             try {
    +                needsManageExternalStorage = Boolean.FALSE;
    +                PackageInfo packageInfo = Launcher4A.loader
    +                      .getPackageManager()
    +                      .getPackageInfo(Launcher4A.loader.getPackageName(), PackageManager.GET_PERMISSIONS);
    +                if (packageInfo.requestedPermissions != null) {
    +                   for (String requestedPermission : packageInfo.requestedPermissions) {
    +                      if (Manifest.permission.MANAGE_EXTERNAL_STORAGE.compareTo(requestedPermission) == 0) {
    +                         needsManageExternalStorage = Boolean.TRUE;
    +                      }
    +                   }
    +                }
    +             } catch (PackageManager.NameNotFoundException e) {
    +                e.printStackTrace();
    +             }
    +          }
    +          if (needsManageExternalStorage == Boolean.TRUE) {
    +             needsManageExternalStorage = !Launcher4A.loader.requestManageStorageAccess();
    +          }
    +       }
    +
    +       return STORAGE.requestPermissions();
         }
     
         public static int requestCameraPermission() {
    @@ -1873,4 +2131,10 @@ public static void playVideo(String id, boolean autoPlay, int start, int end) {
            loader.playVideo(id, autoPlay, start, end);
         }
     
    +    public static int requestBluetoothPermission() {
    +      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    +        return BLUETOOTH.requestPermissions();
    +      }
    +      return PermissionHandler.GRANTED;
    +    }
     }
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/AdjustedInsetsActivity.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/AdjustedInsetsActivity.java
    new file mode 100644
    index 0000000000..51c3634553
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/AdjustedInsetsActivity.java
    @@ -0,0 +1,61 @@
    +package totalcross.android;
    +
    +import android.os.Bundle;
    +import android.view.View;
    +
    +import androidx.annotation.Nullable;
    +import androidx.appcompat.app.AppCompatActivity;
    +import androidx.core.graphics.Insets;
    +import androidx.core.view.ViewCompat;
    +import androidx.core.view.WindowCompat;
    +import androidx.core.view.WindowInsetsCompat;
    +
    +public class AdjustedInsetsActivity extends AppCompatActivity {
    +
    +    @Override
    +    protected void onCreate(@Nullable Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        adjustToSafeArea();
    +    }
    +
    +    private void adjustToSafeArea() {
    +        WindowCompat.setDecorFitsSystemWindows(
    +                getWindow(),
    +                false
    +        );
    +        View rootView = getWindow().getDecorView();
    +
    +        ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, insets) -> {
    +
    +            WindowInsetsCompat rootInsets =
    +                    ViewCompat.getRootWindowInsets(view);
    +
    +            if (rootInsets == null) {
    +                return insets;
    +            }
    +
    +            Insets safeInsets = rootInsets.getInsetsIgnoringVisibility(
    +                    WindowInsetsCompat.Type.systemBars()
    +                            | WindowInsetsCompat.Type.displayCutout()
    +            );
    +
    +            Insets imeInsets = insets.getInsets(
    +                    WindowInsetsCompat.Type.ime()
    +            );
    +
    +            int bottomInset = Math.max(
    +                    safeInsets.bottom,
    +                    imeInsets.bottom
    +            );
    +
    +            view.setPadding(
    +                    safeInsets.left,
    +                    safeInsets.top,
    +                    safeInsets.right,
    +                    bottomInset
    +            );
    +
    +            return insets;
    +        });
    +    }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/CameraViewer.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/CameraViewer.java
    index cbc31a822b..c80e689d14 100644
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/CameraViewer.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/CameraViewer.java
    @@ -3,331 +3,334 @@
     //
     // SPDX-License-Identifier: LGPL-2.1-only
     
    +package totalcross.android;
     
    +import android.Manifest;
    +import android.content.Context;
    +import android.content.pm.PackageManager;
    +import android.graphics.Bitmap;
    +import android.graphics.BitmapFactory;
    +import android.graphics.Matrix;
    +import android.net.Uri;
    +import android.os.Bundle;
    +import android.util.Size;
    +import android.view.ScaleGestureDetector;
    +import android.view.View;
    +import android.widget.Button;
     
    -package totalcross.android;
    +import androidx.annotation.NonNull;
    +import androidx.camera.core.Camera;
    +import androidx.camera.core.CameraSelector;
    +import androidx.camera.core.ImageCapture;
    +import androidx.camera.core.ImageCaptureException;
    +import androidx.camera.core.ImageProxy;
    +import androidx.camera.core.Preview;
    +import androidx.camera.lifecycle.ProcessCameraProvider;
    +import androidx.camera.video.FileOutputOptions;
    +import androidx.camera.video.Quality;
    +import androidx.camera.video.QualitySelector;
    +import androidx.camera.video.Recorder;
    +import androidx.camera.video.Recording;
    +import androidx.camera.video.VideoCapture;
    +import androidx.camera.video.VideoRecordEvent;
    +import androidx.camera.view.PreviewView;
    +import androidx.core.app.ActivityCompat;
    +import androidx.core.content.ContextCompat;
    +
    +import com.google.common.util.concurrent.ListenableFuture;
    +
    +import java.io.File;
    +import java.io.IOException;
    +import java.io.OutputStream;
    +import java.nio.ByteBuffer;
    +import java.util.Objects;
    +import java.util.concurrent.ExecutionException;
    +import java.util.concurrent.Executor;
     
    -import java.io.*;
    -import java.lang.reflect.Method;
     import totalcross.AndroidUtils;
    -import android.app.*;
    -import android.content.*;
    -import android.content.pm.*;
    -import android.graphics.*;
    -import android.hardware.Camera.PictureCallback;
    -import android.hardware.Camera;
    -import android.media.*;
    -import android.os.*;
    -import android.view.*;
    -import android.view.View.OnClickListener;
    -import android.widget.*;
    -import android.content.res.*;
    -
    -public class CameraViewer extends Activity // guich@tc126_34
    -{
    -   class Preview extends SurfaceView implements SurfaceHolder.Callback
    -   { 
    -      Preview(Context context)
    -      {
    -         super(context);
    -		 if (allowRotation) 
    -		 { 
    -			switch (getResources().getConfiguration().orientation)
    -			{
    -			   case Configuration.ORIENTATION_PORTRAIT:
    -			      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    -				  break;
    -			   case Configuration.ORIENTATION_LANDSCAPE:
    -			      AndroidUtils.debug("" + getWindowManager().getDefaultDisplay().getRotation());
    -			      if (getWindowManager().getDefaultDisplay().getRotation() == Surface.ROTATION_270)
    -				     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); 
    -				  else 
    -				     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 
    -		    }
    -		 }
    -		 else
    -            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 
    -         // Install a SurfaceHolder.Callback so we get notified when the
    -         // underlying surface is created and destroyed.
    -         holder = getHolder(); 
    -         holder.addCallback(this); 
    -         holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // DON'T REMOVE THIS! 2.3 (LEVEL 10) STILL REQUIRES IT 
    -      }
    -
    -      // Called once the holder is ready
    -      public void surfaceCreated(SurfaceHolder holder)
    -      {
    -         startPreview();
    -      }
    -
    -      // Called when the holder is destroyed
    -      public void surfaceDestroyed(SurfaceHolder holder)
    -      {
    -         stopPreview();
    -         stopRecording();
    -         holder.removeCallback(this);
    -      }
    -
    -      // Called when holder has changed
    -      public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
    -      { 
    -         if (camera != null)
    -         {
    -            Camera.Parameters parameters = camera.getParameters();
    -			
    -   			Camera.CameraInfo info = new Camera.CameraInfo();
    -            Camera.getCameraInfo(cameraId, info);
    -            int rotation = getWindowManager().getDefaultDisplay().getRotation();
    -            int degrees = 0;
    -            switch (rotation) 
    -            {
    -               case Surface.ROTATION_0: 
    -                  degrees = 0; 
    -                  break;
    -               case Surface.ROTATION_90: 
    -                  degrees = 90; 
    -                  break;
    -               case Surface.ROTATION_180: 
    -                  degrees = 180; 
    -                  break;
    -               case Surface.ROTATION_270: 
    -                  degrees = 270;
    -            }
     
    -            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) 
    -            {
    -               result = (info.orientation + degrees) % 360;
    -               result = (360 - result) % 360;  // compensate the mirror
    -            } else {  // back-facing
    -               result = (info.orientation - degrees + 360) % 360;
    -            }
    -            camera.setDisplayOrientation(result);
    -		
    -            parameters.setPictureFormat(PixelFormat.JPEG);
    -            if (Build.VERSION.SDK_INT >= 14 && getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS) && !inFocusExclusionList())
    -               parameters.setFocusMode("continuous-picture"); // FOCUS_MODE_CONTINUOUS_PICTURE 
    -            if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH))
    -               parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
    -            int ww = Math.max(width,height);
    -            int hh = Math.min(width,height);
    -//            parameters.setPreviewSize(ww,hh);
    -            parameters.setJpegQuality(stillQuality == 1 ? 75 : stillQuality == 2 ? 85 : 100);
    -            if (width != 0 && height != 0)
    -               parameters.setPictureSize(ww,hh);
    -            try
    -            {
    -               camera.setParameters(parameters);
    -            }
    -            catch (RuntimeException re)
    -            {
    -               AndroidUtils.handleException(re,false);
    -            }
    -            camera.startPreview();
    -         }
    -      }
    -
    -      private boolean inFocusExclusionList()
    -      {
    -         String id = Settings4A.deviceId;
    -         return id.indexOf("GT-S7580") != -1;
    -      }
    -   }
    -
    -   SurfaceHolder holder; 
    -   Camera camera; 
    -   boolean isMovie;
    -   boolean allowRotation;
    -   String fileName;
    -   int stillQuality, width,height;
    -   Preview preview; 
    -   MediaRecorder recorder;
    -   int cameraId;
    -   int result;
    -
    -   private void startPreview()
    -   {
    -      if (camera == null)
    -         try
    -         {
    -            // The Surface has been created, acquire the camera and tell it where to draw.
    -            if ((camera = Camera.open()) == null)
    -            {
    -               Method getNumberOfCameras = android.hardware.Camera.class.getMethod("getNumberOfCameras");
    -               if (getNumberOfCameras != null)
    -               {
    -                  int i = (Integer) getNumberOfCameras.invoke(null, (Object[]) null);
    -                  Method open = android.hardware.Camera.class.getMethod("open", int.class);
    -                  if (open != null)
    -                     while (--i >= 0)
    -                        if ((camera = (Camera) open.invoke(null, i)) != null)
    -                        {
    -						   cameraId = i;
    -						   break;
    -						}
    -               }
    +public class CameraViewer extends AdjustedInsetsActivity {
    +    private ListenableFuture cameraProviderFuture;
    +    PreviewView previewView;
    +    private ImageCapture imageCapture;
    +    private VideoCapture videoCapture;
    +    private Recording recorder;
    +    private Camera camera;
    +
    +    boolean isMovie;
    +    String fileName;
    +    int stillQuality, width, height;
    +
    +    boolean allowRotation;
    +
    +    /**
    +     * Called when the activity is first created.
    +     */
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +        setTitle("");
    +
    +        Bundle b = getIntent().getExtras();
    +        fileName = b.getString("file");
    +        stillQuality = b.getInt("quality");
    +        width = b.getInt("width");
    +        height = b.getInt("height");
    +        allowRotation = b.getBoolean("allowRotation");
    +
    +        isMovie = fileName.endsWith(".3gp");
    +
    +        previewView = findViewById(R.id.previewView);
    +
    +        final Button buttonExit = (Button) findViewById(R.id.buttonExit);
    +        buttonExit.setText("Exit");
    +        buttonExit.setOnClickListener(new View.OnClickListener() {
    +            public void onClick(View v) {
    +                stopRecording();
    +                setResult(RESULT_CANCELED);
    +                finish();
                 }
    -            camera.setPreviewDisplay(holder);
    -         }
    -         catch (Exception e)
    -         {
    -            AndroidUtils.handleException(e,false);
    -            setResult(RESULT_CANCELED);
    -            finish();
    -         }
    -   }
    -
    -   private void stopPreview()
    -   {
    -      if (camera != null)
    -      {
    -         try {camera.stopPreview();} catch (Exception e) {e.printStackTrace();}
    -         try {camera.release();} catch (Exception e) {e.printStackTrace();}
    -         camera = null;
    -      }
    -   }
    -   
    -   private void startRecording() throws IllegalStateException, IOException
    -   {     
    -      try {camera.stopPreview();} catch (Exception e) {e.printStackTrace();} // stop camera's preview
    -	   recorder = new MediaRecorder();
    -	   camera.unlock();
    -	   recorder.setCamera(camera);
    -      recorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
    -      recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
    -      recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    -      recorder.setVideoEncoder (MediaRecorder.VideoEncoder.DEFAULT);
    -      recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);     
    -	   recorder.setVideoSize(320,240);
    -     
    -      recorder.setOutputFile(fileName);
    -      recorder.setPreviewDisplay(holder.getSurface());
    -
    -      recorder.prepare();
    -      recorder.start();   // Recording is now started	  
    -   }
    -   
    -   private void stopRecording()
    -   {
    -      if (recorder != null)
    -      {
    -         try {recorder.stop();} catch (Exception e) {e.printStackTrace();}         
    -		   try {recorder.reset();} catch (Exception e) {e.printStackTrace();}   // You can reuse the object by going back to setAudioSource() step
    -		   try {recorder.release();} catch (Exception e) {e.printStackTrace();} // Now the object cannot be reused
    -		   recorder = null;
    -		   camera.lock();	
    -		   stopPreview();
    -      }  
    -   }
    -   
    -   /** Called when the activity is first created. */
    -   public void onCreate(Bundle savedInstanceState)
    -   {
    -      super.onCreate(savedInstanceState);
    -      setContentView(R.layout.main);
    -      setTitle("");
    -      Bundle b = getIntent().getExtras();
    -      fileName = b.getString("file");
    -      stillQuality = b.getInt("quality");
    -      width = b.getInt("width");
    -      height = b.getInt("height");
    -	  allowRotation = b.getBoolean("allowRotation");
    -
    -      isMovie = fileName.endsWith(".3gp");
    -
    -      preview = new Preview(this); 
    -      ((FrameLayout) findViewById(R.id.preview)).addView(preview);
    -
    -      final Button buttonExit = (Button) findViewById(R.id.buttonExit);
    -      buttonExit.setOnClickListener(new OnClickListener()
    -      {
    -         public void onClick(View v)
    -         {
    -			   stopRecording();
    -            setResult(RESULT_CANCELED);
    -            finish();
    -         }
    -      });
    -      
    -      final Button buttonClick = (Button) findViewById(R.id.buttonClick);
    -      buttonClick.setText(isMovie ? "Start" : "Click");
    -      buttonExit.setText("Exit");
    -      buttonClick.setOnClickListener(new OnClickListener()
    -      {
    -         public void onClick(View v)
    -         {
    -            try
    -            {
    -               if (!isMovie)
    -               {
    -                  if (camera == null) // guich@tc130: prevent NPE
    -                     startPreview();
    -                  if (camera != null)
    -                  {
    -                     buttonClick.setClickable(false);
    -                     Camera.Parameters parameters = camera.getParameters();
    -                     parameters.setRotation(rotation);
    -                     try {
    -                    	 camera.setParameters(parameters);
    -                    	 camera.reconnect();
    -                     } catch (Exception re) {
    -                    	 AndroidUtils.handleException(re, false);
    -                     }
    -                     camera.takePicture(null, null, jpegCallback);
    -                  }
    -                  else
    -                  {
    -                     setResult(RESULT_CANCELED);
    -                     finish();
    -                  }
    -               }
    -               else
    -               {
    -                  if (recorder == null)
    -                  {
    -                     buttonClick.setText("Stop");
    -                     startRecording();
    -                  }
    -                  else
    -                  {
    -                     stopRecording();
    -                     setResult(RESULT_OK);
    -                     finish();
    -                  }
    -               }
    +        });
    +
    +        final Button buttonClick = (Button) findViewById(R.id.buttonClick);
    +        if (isMovie) {
    +            buttonClick.setText("Start");
    +            buttonClick.setOnClickListener(new View.OnClickListener() {
    +                public void onClick(View v) {
    +                    try {
    +                        if (buttonClick.getText() == "Start") {
    +                            buttonClick.setText("Stop");
    +                            File f = new File(fileName);
    +                            recordVideo(f);
    +                        } else {
    +                            stopRecording();
    +                        }
    +//                  if (recorder == null) {
    +//                     buttonClick.setText("Stop");
    +//                     startRecording();
    +//                  } else {
    +//                     stopRecording();
    +//                     setResult(RESULT_OK);
    +//                     finish();
    +//                  }
    +                    } catch (Exception e) {
    +                        AndroidUtils.handleException(e, false);
    +                        setResult(RESULT_CANCELED);
    +                        finish();
    +                    }
    +                }
    +            });
    +        } else {
    +            buttonClick.setText("Click");
    +            buttonClick.setOnClickListener(new View.OnClickListener() {
    +                public void onClick(View v) {
    +                    try {
    +                        OutputStream outStream = getContentResolver().openOutputStream(Uri.fromFile(new File(fileName)));
    +                        capturePhoto(outStream);
    +//                  if (camera == null) // guich@tc130: prevent NPE
    +//                     startPreview();
    +//                  if (camera != null) {
    +//                     buttonClick.setClickable(false);
    +//                     Camera.Parameters parameters = camera.getParameters();
    +//                     parameters.setRotation(exifRotation);
    +//                     try {
    +//                        camera.setParameters(parameters);
    +//                        camera.reconnect();
    +//                     } catch (Exception re) {
    +//                        AndroidUtils.handleException(re, false);
    +//                     }
    +//                     camera.takePicture(null, null, jpegCallback);
    +//                  } else {
    +//                     setResult(RESULT_CANCELED);
    +//                     finish();
    +//                  }
    +                    } catch (Exception e) {
    +                        AndroidUtils.handleException(e, false);
    +//                  stopPreview();
    +                        setResult(RESULT_CANCELED);
    +                        finish();
    +                    }
    +                }
    +            });
    +        }
    +
    +        cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    +        cameraProviderFuture.addListener(() -> {
    +            try {
    +                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
    +                startCameraX(cameraProvider);
    +            } catch (ExecutionException e) {
    +                e.printStackTrace();
    +            } catch (InterruptedException e) {
    +                e.printStackTrace();
                 }
    -            catch (Exception e) 
    -            {
    -               AndroidUtils.handleException(e,false);
    -               stopPreview();
    -               setResult(RESULT_CANCELED);
    -               finish();
    +
    +        }, getExecutor());
    +    }
    +
    +    private Executor getExecutor() {
    +        return ContextCompat.getMainExecutor(this);
    +    }
    +
    +    private void startCameraX(ProcessCameraProvider cameraProvider) {
    +
    +        cameraProvider.unbindAll();
    +
    +        CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
    +
    +        Preview preview = new Preview.Builder().build();
    +
    +        preview.setSurfaceProvider(previewView.getSurfaceProvider());
    +
    +        final boolean isLandscape =
    +                getResources().getConfiguration().screenWidthDp >
    +                        getResources().getConfiguration().screenHeightDp;
    +
    +        imageCapture = new ImageCapture.Builder()
    +                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
    +                .setFlashMode(ImageCapture.FLASH_MODE_AUTO)
    +                .setJpegQuality(stillQuality == 1 ? 75 : stillQuality == 2 ? 85 : 100)
    +                /*
    +                    We'll keep using setTargetResolution for the time being, because the preferred
    +                    method using ResolutionSelector, as of now, only allows selecting 4:3 or 16:9
    +                    resolutions.
    +                 */
    +                .setTargetResolution(
    +                        /*
    +                            setTargetResolution picks resolution based on the current orientation
    +                            of the application, so in landscape we have to invert the dimensions to
    +                            pick the correct resolution.
    +                         */
    +                        isLandscape ?
    +                                new Size(width, height) :
    +                                new Size(height, width))
    +                .build();
    +
    +        videoCapture = VideoCapture.withOutput(new Recorder.Builder()
    +                .setQualitySelector(QualitySelector.from(Quality.HD))
    +                .build());
    +
    +        camera = cameraProvider.bindToLifecycle(
    +                this,
    +                cameraSelector,
    +                preview,
    +                imageCapture,
    +                videoCapture
    +        );
    +
    +        enablePinchToZoom(this, camera, previewView);
    +    }
    +
    +    private void recordVideo(File f) {
    +        if (videoCapture != null) {
    +            if (ActivityCompat.checkSelfPermission(
    +                    this, Manifest.permission.RECORD_AUDIO) !=
    +                    PackageManager.PERMISSION_GRANTED) {
    +                return;
                 }
    -         }
    -      });
    -   }
    -   
    -   int rotation = 0;
    -
    -   // Handles data for jpeg picture
    -   PictureCallback jpegCallback = new PictureCallback()
    -   {
    -      public void onPictureTaken(byte[] data, Camera camera)
    -      {
    -         try
    -         {
    -            FileOutputStream outStream = new FileOutputStream(fileName); 
    -            outStream.write(data);
    -            outStream.close();
    -            Loader.autoRotatePhoto(fileName);
    -            setResult(RESULT_OK);
    -            finish();
    -         }
    -         catch (Exception e)
    -         {
    -            AndroidUtils.handleException(e,false);
    -            setResult(RESULT_CANCELED);
    -            finish();
    -         }
    -      }
    -   };
    +
    +            recorder = videoCapture.getOutput()
    +                    .prepareRecording(this,
    +                            new FileOutputOptions.Builder(f).build())
    +                    .withAudioEnabled()
    +                    .start(getExecutor(), event -> {
    +                        if (event instanceof VideoRecordEvent.Finalize) {
    +                            setResult(RESULT_OK);
    +                            finish();
    +                        }
    +                    });
    +        }
    +    }
    +
    +    private Bitmap imageProxyToBitmap(ImageProxy image) {
    +        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    +        byte[] bytes = new byte[buffer.remaining()];
    +        buffer.get(bytes);
    +        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    +    }
    +
    +    private Bitmap rotateBitmap(Bitmap bitmap, int rotationDegrees) {
    +        Matrix matrix = new Matrix();
    +        matrix.postRotate(rotationDegrees);
    +        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    +    }
    +
    +    private void saveBitmap(Bitmap bitmap, OutputStream outStream) throws IOException {
    +        bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outStream);
    +        outStream.flush();
    +        outStream.close();
    +    }
    +
    +    private void capturePhoto(OutputStream outStream) {
    +//      long timeStamp = System.currentTimeMillis();
    +//      ContentValues contentValues = new ContentValues();
    +//      contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, timeStamp);
    +//      contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
    +
    +        imageCapture.takePicture(
    +                getExecutor(),
    +                new ImageCapture.OnImageCapturedCallback() {
    +                    @Override
    +                    public void onCaptureSuccess(@NonNull ImageProxy image) {
    +//                    Toast.makeText(CameraViewer.this,"Saving...",Toast.LENGTH_SHORT).show();
    +                        int rotation = image.getImageInfo().getRotationDegrees();
    +
    +                        // 1. Convert ImageProxy to Bitmap
    +                        Bitmap bmp = imageProxyToBitmap(image);
    +
    +                        // 2. Adjust rotation
    +                        bmp = rotateBitmap(bmp, rotation);
    +
    +                        try {
    +                            saveBitmap(bmp, outStream);
    +                            setResult(RESULT_OK);
    +                            finish();
    +                        } catch (IOException e) {
    +                            AndroidUtils.handleException(e, false);
    +                        }
    +
    +                        image.close();
    +                    }
    +
    +                    @Override
    +                    public void onError(@NonNull ImageCaptureException exception) {
    +//                    Toast.makeText(CameraViewer.this,"Error: "+exception.getMessage(),Toast.LENGTH_SHORT).show();
    +                        AndroidUtils.handleException(exception, false);
    +                        setResult(RESULT_CANCELED);
    +                        finish();
    +                    }
    +                });
    +
    +    }
    +
    +    private void stopRecording() {
    +        if (recorder != null) {
    +            recorder.stop();
    +            recorder = null;
    +        }
    +    }
    +
    +    /* =========================
    +    🔍 PINCH TO ZOOM
    +    ========================= */
    +    private void enablePinchToZoom(Context context, Camera camera, PreviewView previewView) {
    +        ScaleGestureDetector detector =
    +                new ScaleGestureDetector(context,
    +                        new ScaleGestureDetector.SimpleOnScaleGestureListener() {
    +                            @Override
    +                            public boolean onScale(@NonNull ScaleGestureDetector d) {
    +                                float scale =
    +                                        Objects.requireNonNull(camera.getCameraInfo().getZoomState()
    +                                                .getValue()).getZoomRatio() * d.getScaleFactor();
    +                                camera.getCameraControl().setZoomRatio(scale);
    +                                return true;
    +                            }
    +                        });
    +
    +        previewView.setOnTouchListener((v, e) -> {
    +            detector.onTouchEvent(e);
    +            return true;
    +        });
    +    }
     }
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/GPSHelper.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/GPSHelper.java
    index 5d52916595..47976f8f44 100644
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/GPSHelper.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/GPSHelper.java
    @@ -1,18 +1,33 @@
     package totalcross.android;
     
    -import totalcross.*;
    -
    -import android.content.*;
    -import android.location.*;
    -import android.os.*;
    -import com.google.android.gms.common.*;
    -import com.google.android.gms.common.api.*;
    -import com.google.android.gms.common.api.GoogleApiClient.*;
    -import com.google.android.gms.location.*;
    +import android.content.Context;
    +import android.location.Location;
    +import android.location.LocationManager;
    +import android.os.Bundle;
    +import android.os.Message;
    +
    +import androidx.annotation.NonNull;
    +import androidx.core.content.ContextCompat;
    +import androidx.core.location.GnssStatusCompat;
    +import androidx.core.location.LocationManagerCompat;
    +
    +import com.google.android.gms.common.ConnectionResult;
    +import com.google.android.gms.common.GoogleApiAvailability;
    +import com.google.android.gms.common.api.GoogleApiClient;
    +import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
    +import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
     import com.google.android.gms.location.LocationListener;
    -import java.util.*;
    +import com.google.android.gms.location.LocationRequest;
    +import com.google.android.gms.location.LocationServices;
     
    -public class GPSHelper implements android.location.LocationListener, GpsStatus.Listener, LocationListener, ConnectionCallbacks, OnConnectionFailedListener
    +import java.util.Calendar;
    +import java.util.GregorianCalendar;
    +import java.util.TimeZone;
    +
    +import totalcross.AndroidUtils;
    +import totalcross.Launcher4A;
    +
    +public class GPSHelper extends GnssStatusCompat.Callback implements android.location.LocationListener, LocationListener, ConnectionCallbacks, OnConnectionFailedListener
     {
        public static GPSHelper instance = new GPSHelper();
        private GoogleApiClient googleApiClient;
    @@ -110,22 +125,31 @@ public String gpsTurn(boolean on)
           }
           return null;
        }
    -   
    -   public void onGpsStatusChanged(int event) 
    -   {
    -      if (gps != null && (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS || event == GpsStatus.GPS_EVENT_FIRST_FIX)) 
    -      {
    -        try {
    -         GpsStatus status = gps.getGpsStatus(null);
    -         Iterable sats = status.getSatellites();
    -         validSatellites = 0;
    -         for (GpsSatellite sat : sats)
    -            if (sat.usedInFix())
    -               validSatellites++;
    -        } catch (SecurityException e) {
    -        	e.printStackTrace();
    -        }
    +
    +   public String gpsResetData() {
    +      LocationManager locationmanager = (LocationManager) Launcher4A.loader.getSystemService(Context.LOCATION_SERVICE);
    +      locationmanager.sendExtraCommand(LocationManager.GPS_PROVIDER, "delete_aiding_data", null);
    +      return null;
    +   }
    +
    +   public String gpsDownloadData() {
    +      LocationManager locationmanager = (LocationManager) Launcher4A.loader.getSystemService(Context.LOCATION_SERVICE);
    +      Bundle bundle = new Bundle();
    +      locationmanager.sendExtraCommand(LocationManager.GPS_PROVIDER, "force_xtra_injection", bundle);
    +      locationmanager.sendExtraCommand(LocationManager.GPS_PROVIDER, "force_psds_injection", bundle);
    +      locationmanager.sendExtraCommand(LocationManager.GPS_PROVIDER, "force_time_injection", bundle);
    +      return null;
    +   }
    +
    +   @Override
    +   public void onSatelliteStatusChanged(@NonNull GnssStatusCompat status) {
    +      int usedInFix = 0;
    +      for (int i = 0 ; i < status.getSatelliteCount() ; i++) {
    +         if (status.usedInFix(i)) {
    +            usedInFix++;
    +         }
           }
    +      validSatellites = usedInFix;
        }
     
        public void onProviderDisabled(String provider)   {}
    @@ -145,7 +169,7 @@ public void startGps()
              gps = (LocationManager) Launcher4A.loader.getSystemService(Context.LOCATION_SERVICE);
              try {
              gps.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, instance);
    -         gps.addGpsStatusListener(instance);
    +         LocationManagerCompat.registerGnssStatusCallback(gps, ContextCompat.getMainExecutor(Launcher4A.loader),instance);
     	     } catch (SecurityException e) {
     		     e.printStackTrace();
     		 }
    @@ -165,7 +189,7 @@ public void startGps()
              // try only to get satellite information, since this is not given by Google Play Services
              gps = (LocationManager) Launcher4A.loader.getSystemService(Context.LOCATION_SERVICE);
              try {
    -         gps.addGpsStatusListener(this);
    +            LocationManagerCompat.registerGnssStatusCallback(gps, ContextCompat.getMainExecutor(Launcher4A.loader),instance);
              } catch (SecurityException e) {
              	e.printStackTrace();
              }    
    @@ -181,8 +205,9 @@ public void stopGps()
              LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
              googleApiClient = null;
           }
    -      if (gps != null)
    -         gps.removeGpsStatusListener(this);
    +      if (gps != null) {
    +         LocationManagerCompat.unregisterGnssStatusCallback(gps, this);
    +      }
           gps = null;
           validSatellites = 0;
        }
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/ImagePreviewActivity.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/ImagePreviewActivity.java
    new file mode 100644
    index 0000000000..d687432c27
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/ImagePreviewActivity.java
    @@ -0,0 +1,39 @@
    +package totalcross.android;
    +
    +import android.net.Uri;
    +import android.os.Bundle;
    +import android.widget.ImageButton;
    +import android.widget.ImageView;
    +
    +import java.io.File;
    +
    +public class ImagePreviewActivity extends AdjustedInsetsActivity {
    +
    +    @Override
    +    protected void onCreate(Bundle b) {
    +        super.onCreate(b);
    +        setContentView(R.layout.activity_image_preview);
    +
    +        ImageView img = findViewById(R.id.imageView);
    +
    +        String path = getIntent().getStringExtra("image_path");
    +        if (path == null) {
    +            finish();
    +            return;
    +        }
    +
    +        img.setImageURI(Uri.fromFile(new File(path)));
    +
    +        ImageButton btnBackPlayer = findViewById(R.id.btnBackPlayer);
    +        btnBackPlayer.setOnClickListener(v -> {
    +            setResult(RESULT_CANCELED);
    +            finish();
    +        });
    +
    +        ImageButton btnSalvar = findViewById(R.id.btnSalvar);
    +        btnSalvar.setOnClickListener(view -> {
    +            setResult(RESULT_OK);
    +            finish();
    +        });
    +    }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/Loader.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/Loader.java
    index 358e35082b..b207c0b25b 100644
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/Loader.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/Loader.java
    @@ -5,6 +5,10 @@
     
     package totalcross.android;
     
    +import static android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION;
    +
    +import static androidx.core.view.WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
    +
     import android.speech.tts.TextToSpeech;
     import totalcross.*;
     import totalcross.android.PathUtil;
    @@ -30,9 +34,14 @@
     import android.view.inputmethod.*;
     import android.webkit.MimeTypeMap;
     import android.widget.*;
    -import android.support.v4.app.ActivityCompat;
    -import android.support.v4.content.FileProvider;
    -import com.google.android.gms.ads.*;
    +
    +import androidx.activity.result.ActivityResultLauncher;
    +import androidx.activity.result.contract.ActivityResultContracts;
    +import androidx.annotation.NonNull;
    +import androidx.appcompat.app.AppCompatActivity;
    +import androidx.core.app.ActivityCompat;
    +import androidx.core.content.FileProvider;
    +// import com.google.android.gms.ads.*;
     import com.google.firebase.FirebaseApp;
     import com.google.firebase.FirebaseOptions;
     import com.google.zxing.integration.android.*;
    @@ -43,14 +52,24 @@
     import java.util.concurrent.*;
     import java.text.SimpleDateFormat;
     
    +import com.journeyapps.barcodescanner.ScanContract;
    +import com.journeyapps.barcodescanner.ScanIntentResult;
    +import com.journeyapps.barcodescanner.ScanOptions;
    +
     import org.json.simple.JSONArray;
     import org.json.simple.JSONObject;
     import org.json.simple.parser.JSONParser;
     import org.json.simple.parser.ParseException;
     
    -import  android.support.customtabs.CustomTabsIntent;
    +import androidx.browser.customtabs.CustomTabsIntent;
    +import android.view.View;
    +import androidx.core.view.ViewCompat;
    +import androidx.core.view.WindowCompat;
    +import androidx.core.view.WindowInsetsAnimationCompat;
    +import androidx.core.view.WindowInsetsCompat;
    +import androidx.core.graphics.Insets;
     
    -public class Loader extends Activity implements TextToSpeech.OnInitListener, ActivityCompat.OnRequestPermissionsResultCallback
    +public class Loader extends AppCompatActivity implements TextToSpeech.OnInitListener, ActivityCompat.OnRequestPermissionsResultCallback
     {
       public static boolean IS_EMULATOR = android.os.Build.MODEL.toLowerCase().indexOf("sdk") >= 0;
       public Handler achandler;
    @@ -63,6 +82,7 @@ public class Loader extends Activity implements TextToSpeech.OnInitListener, Act
       private static final int CAMERA_PIC_REQUEST = 1337;
       private static final int SPEECH_TO_TEXT = 1234324336;
       private static final int FROM_SCANDIT = 1234324337;
    +  private static final int APP_STORAGE_ACCESS_REQUEST_CODE = 1234324338;
       private static boolean onMainLoop;
       public static boolean isFullScreen;
     
    @@ -70,6 +90,8 @@ public class Loader extends Activity implements TextToSpeech.OnInitListener, Act
       
       private static final String GOOGLECHROME_NAVIGATE_PREFIX = "googlechrome://navigate?url=";  
     
    +  private ActivityResultLauncher scanQrResultLauncher;
    +
       private static boolean onCreateCalled; //
       /** Called when the activity is first created. */
        public void onCreate(Bundle savedInstanceState)
    @@ -93,6 +115,18 @@ public void onCreate(Bundle savedInstanceState)
           AndroidUtils.debug(stack);
              AndroidUtils.error("An exception was issued when launching the program. Please inform this stack trace to your software's vendor:\n\n"+stack,true);
         }
    +
    +       scanQrResultLauncher = Loader.this.registerForActivityResult(
    +               new ActivityResultContracts.StartActivityForResult(),
    +               resultData -> {
    +                   if (resultData.getResultCode() == RESULT_OK) {
    +                       ScanIntentResult result = ScanIntentResult.parseActivityResult(resultData.getResultCode(), resultData.getData());
    +                       if (result.getContents() != null) {
    +                           Launcher4A.zxingResult = result.getContents();
    +                       }
    +                   }
    +                   Launcher4A.callingZXing = false;
    +               });
       }
     
        public void onRestart()
    @@ -124,6 +158,9 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
        {
           switch (requestCode)
           {
    +          case APP_STORAGE_ACCESS_REQUEST_CODE:
    +              permissionInitialized = resultCode == RESULT_OK ? 1 : -1;
    +              break;
         case FROM_SCANDIT:
                 Launcher4A.zxingResult = data.getBooleanExtra("barcodeRecognized", false) ? data.getStringExtra("barcodeData") : null; //data.getStringExtra("barcodeSymbologyName").toUpperCase());
           Launcher4A.callingZXing = false;
    @@ -134,8 +171,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
           break;
     
         case SELECT_PICTURE:
    -      if (resultCode == RESULT_OK)
    -      {
    +            if (resultCode == RESULT_OK)
    +            {
             Uri uri = data.getData();
             String filePath = "";
             try {
    @@ -147,12 +184,13 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
               
               if (filePath == null || filePath.startsWith("/enc") || !(new File(filePath).exists())) {
                 try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
    -              AndroidUtils.copyStreamToFile(inputStream, imageFN);
    -            } catch (IOException ioe) {
    -              AndroidUtils.handleException(ioe, false);
    -              // pois eh... mesmo assim falhou i.i
    -              resultCode = RESULT_OK + 1;
    -            }
    +          AndroidUtils.copyStreamToFile(inputStream, imageFN);
    +          autoRotatePhoto(getContentResolver(), imageFN);
    +        } catch (IOException ioe) {
    +          AndroidUtils.handleException(ioe, false);
    +          // pois eh... mesmo assim falhou i.i
    +          resultCode = RESULT_OK + 1;
    +        }
               } else {
                 BufferedWriter writer = new BufferedWriter(new FileWriter(imageFN));
                 writer.write(filePath);
    @@ -160,7 +198,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
               }
             } catch (IOException e) {
               e.printStackTrace();
    -        }
    +      }
           }
           Launcher4A.pictureTaken(resultCode != RESULT_OK ? 1 : 0);
           break;
    @@ -178,11 +216,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
           break;
         case MAP_RETURN:
           Launcher4A.showingMap = false;
    -      break;
    -    case IntentIntegrator.REQUEST_CODE:
    -      IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
    -      Launcher4A.zxingResult = result.getContents();
    -      Launcher4A.callingZXing = false;
           break;
             case EXTCAMERA_RETURN: {
                 if (capturedImageURI == null) {
    @@ -204,7 +237,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
                         AndroidUtils.copyFile(capturedImageFilePath, imageFN);
     
                         long date = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_ADDED));
    -                    autoRotatePhoto(imageFN);
    +                    autoRotatePhoto(getContentResolver(), imageFN);
                         // if the file was deleted, delete from database too
                         if (cameraType == CAMERA_NATIVE_NOCOPY) {
                             try {
    @@ -214,9 +247,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
                                     new File(capturedImageFilePath).delete();
                                 } catch (Exception e) {
                                 }
    -                            getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID
    -                                    + "=" + cursor.getString(cursor.getColumnIndexOrThrow(BaseColumns._ID)), null);
    -                            removeLastImageFromGallery(date);
                             } catch (Exception e) {
                                 AndroidUtils.handleException(e, false);
                             }
    @@ -232,48 +262,53 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data)
                 Launcher4A.pictureTaken(resultCode != RESULT_OK ? 1 : 0);
                 break;
             }
    -    }
    +          default:
    +              super.onActivityResult(requestCode, resultCode, data);
    +              break;
    +      }
       }
     
        private void removeLastImageFromGallery(long orig)
        {
    -        try
    -        {
    +      try
    +      {
                 final String[] imageColumns = { MediaStore.Images.Media._ID,
                     MediaStore.Images.Media.DATE_ADDED,
                     MediaStore.Images.Media.DATA
                 };
    -            final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
    -            Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, null, null, imageOrderBy);
    -            if (imageCursor.moveToFirst())
    -            {
    +      final String imageOrderBy = MediaStore.Images.Media._ID + " DESC";
    +         Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, null, null, imageOrderBy);
    +         if (imageCursor.moveToFirst())
    +         {
                     long last = new File(imageCursor.getString(2)).lastModified();
    -                int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
    -                long dif = Math.abs(orig - last);
    +        int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
    +        long dif = Math.abs(orig - last);
                     if (dif < 1000) { // 1 second - usually is less than 10ms
                         getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                 MediaStore.Images.Media._ID + "=?", new String[]{Long.toString(id)});
                     }
    -            }
    -        }
    -        catch (Exception e)
    -        {
    -        AndroidUtils.handleException(e, false);
    -        }
    +         }
    +      }
    +      catch (Exception e)
    +      {
    +      AndroidUtils.handleException(e, false);
    +    }
       }
     
    -   public static void autoRotatePhoto(String imagePath)
    -   {
    -      try
    -      {
    +   public static void autoRotatePhoto(ContentResolver contentResolver, String imagePath) {
    +      try {
           File f = new File(imagePath);
    -      ExifInterface exif = new ExifInterface(f.getPath());
    +        ExifInterface exif = null;
    +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    +          exif = new ExifInterface(contentResolver.openFileDescriptor(Uri.fromFile(f), "r").getFileDescriptor());
    +        } else {
    +          exif = new ExifInterface(f.getPath());
    +        }
           int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
           AndroidUtils.debug(imagePath + " -> " + orientation);
     
           int angle = 0;
    -         switch (orientation)
    -         {
    +        switch (orientation) {
                 case ExifInterface.ORIENTATION_ROTATE_90: angle  = 90;  break;
                 case ExifInterface.ORIENTATION_ROTATE_180: angle = 180; break;
                 case ExifInterface.ORIENTATION_ROTATE_270: angle = 270; break;
    @@ -283,17 +318,15 @@ public static void autoRotatePhoto(String imagePath)
           Matrix mat = new Matrix();
           mat.postRotate(angle);
           BitmapFactory.Options options = new BitmapFactory.Options();
    -      options.inSampleSize = 2;
    +      options.inSampleSize = 1;
     
    -      Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, options);
    +      Bitmap bmp = BitmapFactory.decodeStream(contentResolver.openInputStream(Uri.fromFile(f)), null, options);
           Bitmap bitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
    -      FileOutputStream out = new FileOutputStream(f);
    +      OutputStream out = contentResolver.openOutputStream(Uri.fromFile(f));
           bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);
           out.close();
           AndroidUtils.debug("auto-rotated " + imagePath);
    -      }
    -      catch (Exception e)
    -      {
    +      } catch (Exception e) {
           AndroidUtils.handleException(e, false);
         }
       }
    @@ -384,7 +417,11 @@ private Uri getFileUri4Intent(File file) {
       private static final int CAMERA_NATIVE = 1;
       private static final int CAMERA_NATIVE_NOCOPY = 2;
       private static final int FROM_GALLERY = 3;
    -  private static String[] cameraTypes = { "CUSTOM", "NATIVE", "NATIVE_NOCOPY", "GALLERY", "UNDEFINED" };
    +  private static final int VIDEO_PLAYER = 4;
    +  private static final int VIDEO_RECORDER = 5;
    +  private static final int PICTURE_ONLY = 6;
    +  private static final int CAMERA_FULL = 7;
    +  private static String[] cameraTypes = { "CUSTOM", "NATIVE", "NATIVE_NOCOPY", "GALLERY", "VIDEO_PLAYER", "VIDEO_RECORDER", "PICTURE_ONLY", "CAMERA_FULL", "UNDEFINED" };
       private int cameraType;
       
       private File createImageFile() throws IOException {
    @@ -400,7 +437,7 @@ private File createImageFile() throws IOException {
           return image;
       }
     
    -    private void captureCamera(String s, int quality, int width, int height, boolean allowRotation, int cameraType) {
    +    private void captureCamera(String s, int quality, int width, int height, boolean allowRotation, int cameraType, int videoTimeLimit, int targetFps, int bitrate) {
             try {
                 imageFN = s;
                 this.cameraType = cameraType;
    @@ -408,12 +445,47 @@ private void captureCamera(String s, int quality, int width, int height, boolean
                         + Build.MODEL.replaceAll("\\P{ASCII}", " ");
                 AndroidUtils.debug(
                         "Taking photo " + width + "x" + height + " from "
    -                            + cameraTypes[0 <= cameraType && cameraType <= 3 ? cameraType : 4]);
    +                            + cameraTypes[0 <= cameraType && cameraType <= 5 ? cameraType : 6]);
                 if (cameraType == FROM_GALLERY) {
                     Intent i = new Intent();
                     i.setType("image/*");
                     i.setAction(Intent.ACTION_GET_CONTENT);
                     startActivityForResult(i, SELECT_PICTURE);
    +            } else if (cameraType == VIDEO_PLAYER) {
    +              Intent intent = new Intent(this, Class.forName(totalcrossPKG + ".VideoPlayerActivity"));
    +              intent.putExtra("video_path", s);
    +              intent.putExtra("playback_only", true);
    +              startActivityForResult(intent, TAKE_PHOTO);
    +            } else if (cameraType == VIDEO_RECORDER) {
    +                Intent intent = new Intent(this, Class.forName(totalcrossPKG + ".VideoCaptureActivity"));
    +                intent.putExtra("mode", VideoCaptureActivity.MODE_VIDEO);
    +                intent.putExtra("file", s);
    +                intent.putExtra("width", width);
    +                intent.putExtra("height", height);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_MAX_SECONDS, videoTimeLimit);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_TARGET_FPS, targetFps);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_QUALITY, quality);
    +                intent.putExtra("bitrate", bitrate);
    +                startActivityForResult(intent, TAKE_PHOTO);
    +            } else if (cameraType == PICTURE_ONLY) {
    +                Intent intent = new Intent(this, Class.forName(totalcrossPKG + ".VideoCaptureActivity"));
    +                intent.putExtra("mode", VideoCaptureActivity.MODE_PICTURE);
    +                intent.putExtra("file", s);
    +                intent.putExtra("width", width);
    +                intent.putExtra("height", height);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_QUALITY, quality);
    +                startActivityForResult(intent, TAKE_PHOTO);
    +            } else if (cameraType == CAMERA_FULL) {
    +                Intent intent = new Intent(this, Class.forName(totalcrossPKG + ".VideoCaptureActivity"));
    +                intent.putExtra("mode", VideoCaptureActivity.MODE_FULL);
    +                intent.putExtra("file", s);
    +                intent.putExtra("width", width);
    +                intent.putExtra("height", height);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_MAX_SECONDS, videoTimeLimit);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_TARGET_FPS, targetFps);
    +                intent.putExtra(VideoCaptureActivity.EXTRA_QUALITY, quality);
    +                intent.putExtra("bitrate", bitrate);
    +                startActivityForResult(intent, TAKE_PHOTO);
                 } else if (cameraType == CAMERA_NATIVE || cameraType == CAMERA_NATIVE_NOCOPY) {
                     ContentValues values = new ContentValues();
                     values.put(MediaStore.Images.Media.TITLE, "tctemp.jpg");
    @@ -428,7 +500,7 @@ private void captureCamera(String s, int quality, int width, int height, boolean
                     intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                     intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, outputFileUri);
                     startActivityForResult(intent, CAMERA_PIC_REQUEST);
    -            } else {
    +            } else { // CAMERA CUSTOM
                     Intent intent = new Intent(this, Class.forName(totalcrossPKG + ".CameraViewer"));
                     intent.putExtra("file", s);
                     intent.putExtra("quality", quality);
    @@ -592,8 +664,82 @@ private void runVM()
         mainLayout.addView(mainView);
         setContentView(mainLayout);
         onMainLoop = true;
    +
    +       WindowCompat.setDecorFitsSystemWindows(
    +               getWindow(),
    +               false
    +       );
    +       View rootView = getWindow().getDecorView();
    +
    +       ViewCompat.setWindowInsetsAnimationCallback(
    +               rootView,
    +               new WindowInsetsAnimationCompat.Callback(
    +                       DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
    +
    +                   @NonNull
    +                   @Override
    +                   public WindowInsetsCompat onProgress(
    +                           @NonNull WindowInsetsCompat insets,
    +                           @NonNull List runningAnimations) {
    +
    +                       // Current sip height during animation
    +                       int bottom = insets.getInsets(
    +                               WindowInsetsCompat.Type.ime()
    +                       ).bottom;
    +
    +                       // % of the keyboard shown, calculated using our cached SIP height
    +                       int h = (int) ((double) bottom * 100) / Launcher4A.instance.sipInsetBottom;
    +
    +                       Launcher4A.instance.nativeInitSize(null, -999, h); // signal vm that the keyboard will appear
    +
    +                       return insets;
    +                   }
    +               });
    +
    +       ViewCompat.setOnApplyWindowInsetsListener(rootView, (view, insets) -> {
    +
    +           WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(view);
    +
    +           if (rootInsets == null) {
    +               return insets;
    +           }
    +
    +           Insets safeInsets = rootInsets.getInsetsIgnoringVisibility(
    +                   WindowInsetsCompat.Type.systemBars()
    +                           | WindowInsetsCompat.Type.displayCutout()
    +           );
    +
    +           // IMPORTANT: make sure the view was already resized
    +           view.post(() -> {
    +               Launcher4A.instance.onSafeAreaChanged(safeInsets);
    +           });
    +
    +           Insets imeInsets = insets.getInsets(
    +                   WindowInsetsCompat.Type.ime()
    +           );
    +
    +           boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
    +                   || imeInsets.bottom > 0;
    +
    +           if (imeVisible) {
    +               Launcher4A.sipVisible = true;
    +               Launcher4A.sipWasOpen = false;
    +           }
    +
    +           if (!imeVisible && Launcher4A.sipVisible) {
    +               Launcher4A.sipWasOpen = true;
    +               Launcher4A.sipVisible = false;
    +           }
    +
    +           // If the keyboard is visible, update our cached SIP height
    +           if (imeInsets.bottom > 0) {
    +               Launcher4A.instance.sipInsetBottom = imeInsets.bottom;
    +           }
    +
    +           return WindowInsetsCompat.CONSUMED;
    +       });
       }
    -   
    +
        public static void setMargins (View v, int l, int t, int r, int b) {
          if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
              ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
    @@ -613,7 +759,7 @@ public int getStatusBarHeight() {
     
       RelativeLayout mainLayout;
       public static View mainView;
    -  public static AdView adView;
    +  // public static AdView adView;
     
        class EventHandler extends Handler 
        {
    @@ -630,8 +776,17 @@ public void handleMessage(Message msg)
             dialNumber(nr);
             break;
           case CAMERA:
    -               captureCamera(b.getString("showCamera.fileName"),b.getInt("showCamera.quality"),b.getInt("showCamera.width")
    -                                                               ,b.getInt("showCamera.height"),b.getBoolean("showCamera.allowRotation"),b.getInt("showCamera.cameraType"));
    +               captureCamera(
    +                  b.getString("showCamera.fileName"),
    +                  b.getInt("showCamera.quality"),
    +                  b.getInt("showCamera.width"),
    +                  b.getInt("showCamera.height"),
    +                  b.getBoolean("showCamera.allowRotation"),
    +                  b.getInt("showCamera.cameraType"),
    +                  b.getInt("showCamera.videoTimeLimit"),
    +                  b.getInt("showCamera.targetFps"),
    +                  b.getInt("showCamera.bitrate")
    +                );
             break;
           case TITLE:
             setTitle(b.getString("setDeviceTitle.title"));
    @@ -674,45 +829,34 @@ public void handleMessage(Message msg)
             }
             break;
           }
    -            case ZXING_SCAN:
    -            {
    -        String cmd = b.getString("zxing.mode");
    -               
    -          StringTokenizer st = new StringTokenizer(cmd, "&");
    -          String mode = "SCAN_MODE";
    -          String scanmsg = "";
    -                  while (st.hasMoreTokens())
    -                  {
    -            String s = st.nextToken();
    -            int i = s.indexOf('=');
    -                     if (i == -1) continue;
    -            String s1 = s.substring(0, i);
    -            String s2 = s.substring(i + 1);
    -            if (s1.equalsIgnoreCase("mode"))
    -              mode = s2;
    -                     else
    -                     if (s1.equalsIgnoreCase("msg"))
    -              scanmsg = s2;
    -
    -          IntentIntegrator integrator = new IntentIntegrator(Loader.this);
    -                  if (mode.equalsIgnoreCase("1D"))
    -                  {
    -            integrator.setDesiredBarcodeFormats(IntentIntegrator.ONE_D_CODE_TYPES);
    -                  }
    -                  else if (mode.equalsIgnoreCase("2D"))
    -                  {
    -            integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES);
    -                  }
    -                  else
    -                  {
    -            integrator.setDesiredBarcodeFormats(null);
    -          }
    -          integrator.setPrompt(scanmsg);
    -          integrator.setResultDisplayDuration(1000);
    -          integrator.autoWide(); // Wide scanning rectangle, may work better for 1D barcodes
    -          integrator.setCameraId(0); // Use a specific camera of the device
    -          integrator.initiateScan();
    -        }
    +             case ZXING_SCAN: {
    +                 String cmd = b.getString("zxing.mode");
    +
    +                     StringTokenizer st = new StringTokenizer(cmd, "&");
    +                     String mode = "SCAN_MODE";
    +                     String scanmsg = "";
    +                     while (st.hasMoreTokens()) {
    +                         String s = st.nextToken();
    +                         int i = s.indexOf('=');
    +                         if (i == -1) continue;
    +                         String s1 = s.substring(0, i);
    +                         String s2 = s.substring(i + 1);
    +                         if (s1.equalsIgnoreCase("mode"))
    +                             mode = s2;
    +                         else if (s1.equalsIgnoreCase("msg"))
    +                             scanmsg = s2;
    +                     }
    +
    +                     ScanOptions scanOptions = new ScanOptions();
    +                     if (mode.equalsIgnoreCase("1D")) {
    +                         scanOptions = scanOptions.setDesiredBarcodeFormats(ScanOptions.ONE_D_CODE_TYPES);
    +                     } else if (mode.equalsIgnoreCase("2D")) {
    +                         scanOptions = scanOptions.setDesiredBarcodeFormats(ScanOptions.QR_CODE);
    +                     } else {
    +                         scanOptions = scanOptions.setDesiredBarcodeFormats(ScanOptions.ALL_CODE_TYPES);
    +                     }
    +                     scanOptions = scanOptions.setCaptureActivity(MyCaptureActivity.class);
    +                     scanQrResultLauncher.launch(new ScanContract().createIntent(Loader.this.getApplicationContext(), scanOptions));
             break;
           }
           case TOTEXT:
    @@ -758,69 +902,69 @@ public enum Position
           TOP
       };
     
    -   private AdSize toAdSize(int i)
    -   {
    -      switch (Size.values()[i])
    -      {
    -         case ADMOB_BANNER:  return AdSize.BANNER; 
    -         case ADMOB_FULL:    return AdSize.FULL_BANNER;
    -         case ADMOB_LARGE:   return AdSize.LARGE_BANNER;
    -         case ADMOB_LEADER:  return AdSize.LEADERBOARD;
    -         case ADMOB_MEDIUM:  return AdSize.MEDIUM_RECTANGLE;
    -         case ADMOB_SKY:     return AdSize.WIDE_SKYSCRAPER;
    -         case ADMOB_SMART:   return AdSize.SMART_BANNER;
    -    }
    -    return null;
    -  }
    +  //  private AdSize toAdSize(int i)
    +  //  {
    +  //     switch (Size.values()[i])
    +  //     {
    +  //        case ADMOB_BANNER:  return AdSize.BANNER; 
    +  //        case ADMOB_FULL:    return AdSize.FULL_BANNER;
    +  //        case ADMOB_LARGE:   return AdSize.LARGE_BANNER;
    +  //        case ADMOB_LEADER:  return AdSize.LEADERBOARD;
    +  //        case ADMOB_MEDIUM:  return AdSize.MEDIUM_RECTANGLE;
    +  //        case ADMOB_SKY:     return AdSize.WIDE_SKYSCRAPER;
    +  //        case ADMOB_SMART:   return AdSize.SMART_BANNER;
    +  //   }
    +  //   return null;
    +  // }
     
        private void configureAd(String id)
        {
    -    if (adView != null)
    -      return;
    +    // if (adView != null)
    +    //   return;
     
    -    adView = new AdView(this);
    -    adView.setAdUnitId(id);
    -    adView.setAdSize(defaultAdSize);
    -    adView.setVisibility(adIsVisible ? View.VISIBLE : View.INVISIBLE);
    -    adView.loadAd(new AdRequest.Builder().build());
    +    // adView = new AdView(this);
    +    // adView.setAdUnitId(id);
    +    // adView.setAdSize(defaultAdSize);
    +    // adView.setVisibility(adIsVisible ? View.VISIBLE : View.INVISIBLE);
    +    // adView.loadAd(new AdRequest.Builder().build());
     
           RelativeLayout.LayoutParams adParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
         adParams.addRule(adAtBottom ? RelativeLayout.ALIGN_PARENT_BOTTOM : RelativeLayout.ALIGN_PARENT_TOP);
    -    mainLayout.addView(adView, adParams);
    -
    -      adView.setAdListener(new AdListener()
    -      {
    -      boolean firstAd = true;
    -         public void onAdLoaded()
    -         {
    -        AndroidUtils.debug("onAdLoaded");
    -            if (firstAd && adIsVisible)
    -            {
    -          firstAd = false;
    -          adView.setVisibility(View.GONE);
    -          adView.setVisibility(View.VISIBLE);
    -        }
    -      }
    -         public void onAdFailedToLoad(int errorCode)
    -         {
    -        AndroidUtils.debug("onAdFailedToLoad: " + errorCode);
    -      }
    -         public void onAdOpened()
    -         {
    -        AndroidUtils.debug("onAdOpened");
    -      }
    -         public void onAdClosed()
    -         {
    -        AndroidUtils.debug("onAdClosed");
    -      }
    -         public void onAdLeftApplication()
    -         {
    -        AndroidUtils.debug("onAdLeftApplication");
    -      }
    -    });
    +    // mainLayout.addView(adView, adParams);
    +
    +    //   adView.setAdListener(new AdListener()
    +    //   {
    +    //   boolean firstAd = true;
    +    //      public void onAdLoaded()
    +    //      {
    +    //     AndroidUtils.debug("onAdLoaded");
    +    //         if (firstAd && adIsVisible)
    +    //         {
    +    //       firstAd = false;
    +    //       adView.setVisibility(View.GONE);
    +    //       adView.setVisibility(View.VISIBLE);
    +    //     }
    +    //   }
    +    //      public void onAdFailedToLoad(int errorCode)
    +    //      {
    +    //     AndroidUtils.debug("onAdFailedToLoad: " + errorCode);
    +    //   }
    +    //      public void onAdOpened()
    +    //      {
    +    //     AndroidUtils.debug("onAdOpened");
    +    //   }
    +    //      public void onAdClosed()
    +    //      {
    +    //     AndroidUtils.debug("onAdClosed");
    +    //   }
    +    //      public void onAdLeftApplication()
    +    //      {
    +    //     AndroidUtils.debug("onAdLeftApplication");
    +    //   }
    +    // });
       }
     
    -  private AdSize defaultAdSize = AdSize.SMART_BANNER;
    +  // private AdSize defaultAdSize = AdSize.SMART_BANNER;
       private boolean adAtBottom = true;
       private boolean adIsVisible;
     
    @@ -834,24 +978,24 @@ private void adsFunc(Bundle b)
           switch (b.getInt("func"))
           {
         case Launcher4A.GET_WH:
    -      AdSize as = toAdSize(i);
    -      ret = as.getHeightInPixels(this) * 1000000 + as.getWidthInPixels(this);
    +      // AdSize as = toAdSize(i);
    +      // ret = as.getHeightInPixels(this) * 1000000 + as.getWidthInPixels(this);
           break;
         case Launcher4A.SET_SIZE:
    -      defaultAdSize = toAdSize(i);
    -      if (adView != null)
    -        adView.setAdSize(toAdSize(i));
    +      // defaultAdSize = toAdSize(i);
    +      // if (adView != null)
    +      //   adView.setAdSize(toAdSize(i));
           break;
         case Launcher4A.SET_POSITION:
           adAtBottom = Position.values()[i] == Position.BOTTOM;
           break;
         case Launcher4A.SET_VISIBLE:
           adIsVisible = i == 1;
    -      if (adView != null)
    -        adView.setVisibility(adIsVisible ? View.VISIBLE : View.INVISIBLE);
    +      // if (adView != null)
    +      //   adView.setVisibility(adIsVisible ? View.VISIBLE : View.INVISIBLE);
           break;
         case Launcher4A.IS_VISIBLE:
    -      ret = adView != null && adView.isShown() ? 1 : 0;
    +      // ret = adView != null && adView.isShown() ? 1 : 0;
           break;
         case Launcher4A.CONFIGURE: // must be last step! 
           configureAd(s);
    @@ -989,18 +1133,18 @@ else if (command.equalsIgnoreCase("url") && args != null) {
                }
                if (i == null) {
                  i = new Intent(Intent.ACTION_VIEW);
    -             if (intentPackage != null) {
    -               i.setPackage(intentPackage);
    -             }
    +           if (intentPackage != null) {
    +             i.setPackage(intentPackage);
    +           }
                }
                if (i != null) {
    -             if (intentData != null && intentType != null) {
    -               i.setDataAndType(Uri.parse(intentData), intentType);
    -             } else if (intentData != null) {
    -               i.setData(Uri.parse(intentData));
    -             }
    -             startActivity(i);
    +           if (intentData != null && intentType != null) {
    +             i.setDataAndType(Uri.parse(intentData), intentType);
    +           } else if (intentData != null) {
    +             i.setData(Uri.parse(intentData));
                }
    +           startActivity(i);
    +         }
               }
              else
              if (command.toLowerCase().endsWith(".apk"))
    @@ -1293,6 +1437,7 @@ public static interface PermissionRequestCodes {
             public static int ACCESS_FINE_LOCATION = 1;
             public static int EXTERNAL_STORAGE = 2;
             public static int CAMERA = 3;
    +        public static int BLUETOOTH = 4;
         }
     
         public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    @@ -1330,4 +1475,21 @@ public void playVideo(String id, boolean autoPlay, int start, int end) {
           }
           startActivity(intent);
         }
    +    
    +    private int permissionInitialized = 0;
    +
    +    public boolean requestManageStorageAccess() {
    +      if (Environment.isExternalStorageManager()) {
    +        return true;
    +      }
    +      Intent intent = new Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package:totalcross." + Launcher4A.instance.tczname));
    +      startActivityForResult(intent, Loader.APP_STORAGE_ACCESS_REQUEST_CODE);
    +      while (permissionInitialized == 0) {
    +        try {
    +          Thread.sleep(10);
    +        } catch (Exception e) {
    +        }
    +      }
    +      return Environment.isExternalStorageManager();
    +    }
     }
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/MyCaptureActivity.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/MyCaptureActivity.java
    new file mode 100644
    index 0000000000..cbb11b6dcd
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/MyCaptureActivity.java
    @@ -0,0 +1,55 @@
    +package totalcross.android;
    +
    +import android.app.Activity;
    +import android.graphics.Color;
    +import android.os.Bundle;
    +import android.view.Gravity;
    +import android.view.View;
    +import android.view.ViewGroup;
    +import android.widget.FrameLayout;
    +import android.widget.ImageButton;
    +
    +import androidx.core.graphics.Insets;
    +import androidx.core.view.ViewCompat;
    +import androidx.core.view.WindowInsetsCompat;
    +
    +import com.journeyapps.barcodescanner.CaptureActivity;
    +
    +public class MyCaptureActivity extends CaptureActivity {
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        View root = findViewById(R.id.zxing_barcode_scanner);
    +
    +        ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
    +            Insets sys = insets.getInsets(WindowInsetsCompat.Type.systemBars());
    +
    +            ImageButton btnBack = new ImageButton(this);
    +            btnBack.setImageResource(R.drawable.ic_close_white);
    +            btnBack.setId(R.id.zxing_back_button);
    +            btnBack.setBackgroundColor(Color.TRANSPARENT);
    +            btnBack.setOnClickListener(l -> {
    +                setResult(Activity.RESULT_CANCELED);
    +                finish();
    +            });
    +
    +            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
    +                    dp(48), dp(48)
    +            );
    +            lp.gravity = Gravity.TOP | Gravity.START;
    +            lp.topMargin = sys.top + dp(16);
    +            lp.leftMargin = dp(16);
    +
    +            ((ViewGroup) v).addView(btnBack, lp);
    +
    +            return insets;
    +        });
    +    }
    +
    +    private int dp(int value) {
    +        return Math.round(
    +                value * getResources().getDisplayMetrics().density
    +        );
    +    }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/NotificationManager4A.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/NotificationManager4A.java
    index 72d8a85ba0..63ba84af1b 100755
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/NotificationManager4A.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/NotificationManager4A.java
    @@ -4,14 +4,13 @@
     import android.os.Build;
     import android.app.NotificationManager;
     import android.app.NotificationChannel;
    -import android.support.v4.app.NotificationCompat;
    -import android.support.v4.app.NotificationCompat.Builder;
    -import android.support.v4.app.NotificationManagerCompat;
    +import androidx.core.app.NotificationCompat;
    +import androidx.core.app.NotificationManagerCompat;
     import android.app.PendingIntent;
     import android.content.Intent;
     import android.content.Context;
     import totalcross.Launcher4A;
    -import android.support.annotation.RequiresApi;
    +import androidx.annotation.RequiresApi;
     
     public class NotificationManager4A {
     
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/RecordProgressDrawable.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/RecordProgressDrawable.java
    new file mode 100644
    index 0000000000..56ca59c8c2
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/RecordProgressDrawable.java
    @@ -0,0 +1,82 @@
    +package totalcross.android;
    +
    +import android.graphics.Canvas;
    +import android.graphics.Color;
    +import android.graphics.Paint;
    +import android.graphics.RectF;
    +import android.graphics.drawable.Drawable;
    +
    +import androidx.annotation.NonNull;
    +import androidx.annotation.Nullable;
    +
    +public class RecordProgressDrawable extends Drawable {
    +
    +    private final Paint trackPaint;      // Track
    +    private final Paint progressPaint;   // Progress
    +
    +    private float progress = 0f; // 0.0 → 1.0
    +    private final RectF arcRect = new RectF();
    +
    +    public RecordProgressDrawable() {
    +        trackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    +        trackPaint.setStyle(Paint.Style.STROKE);
    +        trackPaint.setStrokeWidth(10f);
    +        trackPaint.setColor(Color.parseColor("#50FFFFFF")); // White 50%
    +
    +        progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    +        progressPaint.setStyle(Paint.Style.STROKE);
    +        progressPaint.setStrokeWidth(10f);
    +        progressPaint.setColor(Color.WHITE);
    +        progressPaint.setStrokeCap(Paint.Cap.ROUND);
    +    }
    +
    +    @Override
    +    public void draw(@NonNull Canvas canvas) {
    +        RectF bounds = new RectF(getBounds());
    +        float padding = 12f;
    +
    +        arcRect.set(
    +                bounds.left + padding,
    +                bounds.top + padding,
    +                bounds.right - padding,
    +                bounds.bottom - padding
    +        );
    +
    +        // Track (complete circle)
    +        canvas.drawArc(arcRect, -90, 360, false, trackPaint);
    +
    +        // Progress (partial arc)
    +        canvas.drawArc(arcRect, -90, progress * 360, false, progressPaint);
    +    }
    +
    +    @Override
    +    public void setAlpha(int alpha) {
    +        trackPaint.setAlpha(alpha);
    +        progressPaint.setAlpha(alpha);
    +        invalidateSelf();
    +    }
    +
    +    @Override
    +    public void setColorFilter(@Nullable android.graphics.ColorFilter colorFilter) {
    +        trackPaint.setColorFilter(colorFilter);
    +        progressPaint.setColorFilter(colorFilter);
    +        invalidateSelf();
    +    }
    +
    +    @Override
    +    public int getOpacity() {
    +        return android.graphics.PixelFormat.TRANSLUCENT;
    +    }
    +
    +    /** Updates progress between 0f and 1f */
    +    public void setProgress(float value) {
    +        progress = Math.max(0f, Math.min(1f, value));
    +        invalidateSelf();
    +    }
    +
    +    /** Changes progress color */
    +    public void setProgressColor(int color) {
    +        progressPaint.setColor(color);
    +        invalidateSelf();
    +    }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/Settings4A.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/Settings4A.java
    index 61f1ecee2f..33ac2614a7 100644
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/Settings4A.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/Settings4A.java
    @@ -13,13 +13,13 @@
     import android.os.*;
     import android.provider.*;
     import android.telephony.*;
    -import android.content.pm.PackageManager;
    +import android.media.MediaDrm;
    +import android.media.UnsupportedSchemeException;
    +
     import java.lang.reflect.*;
     import java.util.*;
     import java.net.NetworkInterface;
    -import android.support.v4.content.ContextCompat;
    -import android.support.v4.app.ActivityCompat;
    -import android.Manifest;
    +import java.util.UUID;
     
     import totalcross.*;
     
    @@ -65,6 +65,7 @@ public final class Settings4A
        public static String serialNumber;
        public static String macAddress;
        public static String ANDROID_ID;
    +   public static String UNIQUE_ID;
     
        // device capabilities
        public static boolean virtualKeyboard;
    @@ -184,7 +185,19 @@ static void fillSettings()
              serialNumber = String.valueOf(((long)macAddress.replace(":","").hashCode() & 0xFFFFFFFFFFFFFFL));
           
           ANDROID_ID = Settings.Secure.getString(Launcher4A.loader.getContentResolver(), Settings.Secure.ANDROID_ID);
    -      
    +
    +      final UUID COMMON_PSSH_UUID = new UUID(0x1077EFECC0B24D02L, 0xACE33C1E52E2FB4BL);
    +      final UUID CLEARKEY_UUID = new UUID(0xE2719D58A985B3C9L, 0x781AB030AF78D30EL);
    +      final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
    +      final UUID PLAYREADY_UUID = new UUID(0x9A04F07998404286L, 0xAB92E65BE0885F95L);
    +
    +      try {
    +          MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
    +          UNIQUE_ID = AndroidUtils.encode(mediaDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID));
    +      } catch (UnsupportedSchemeException e) {
    +          AndroidUtils.handleException(e, false);
    +      }
    +
           // virtualKeyboard
           virtualKeyboard = true; // always available on droid?
           
    @@ -264,6 +277,16 @@ private static char getFirstSymbol(String s)
           return ' ';
        }
     
    +    private static boolean isRestrictedIdentifierAccess(Throwable t) {
    +        while (t != null) {
    +            if (t instanceof SecurityException) {
    +                return true;
    +            }
    +            t = t.getCause();
    +        }
    +        return false;
    +    }
    +
         public static void fillTelephonySettings() {
             String id1, id2;
             // imei
    @@ -274,6 +297,10 @@ public static void fillTelephonySettings() {
             } catch (SecurityException e) {
                 e.printStackTrace();
             }
    +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    +            telephonyInitialized = true;
    +            return;
    +        }
             // handle dual-sim phones. Usually, they overload the method with a
             Class cc = telephonyMgr.getClass();
             Method[] mtds = cc.getDeclaredMethods();
    @@ -299,7 +326,9 @@ public static void fillTelephonySettings() {
                             break;
                         }
                     } catch (Exception ee) {
    -                    AndroidUtils.handleException(ee, false);
    +                    if (!isRestrictedIdentifierAccess(ee)) {
    +                        AndroidUtils.handleException(ee, false);
    +                    }
                     }
                 } else if (name.startsWith("getSimSerialNumber") && signat.endsWith("(int)")) {
                     try {
    @@ -312,7 +341,9 @@ public static void fillTelephonySettings() {
                             break;
                         }
                     } catch (Exception ee) {
    -                    AndroidUtils.handleException(ee, false);
    +                    if (!isRestrictedIdentifierAccess(ee)) {
    +                        AndroidUtils.handleException(ee, false);
    +                    }
                     }
                 }
             }
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/VideoCaptureActivity.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/VideoCaptureActivity.java
    new file mode 100644
    index 0000000000..97992ceb86
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/VideoCaptureActivity.java
    @@ -0,0 +1,631 @@
    +package totalcross.android;
    +
    +import static android.view.View.INVISIBLE;
    +import static android.view.View.VISIBLE;
    +
    +import android.Manifest;
    +import android.content.Context;
    +import android.content.Intent;
    +import android.content.pm.PackageManager;
    +import android.graphics.Bitmap;
    +import android.graphics.BitmapFactory;
    +import android.graphics.Color;
    +import android.graphics.Matrix;
    +import android.net.Uri;
    +import android.os.Bundle;
    +import android.os.Environment;
    +import android.os.Handler;
    +import android.os.Looper;
    +import android.util.Size;
    +import android.view.MotionEvent;
    +import android.view.OrientationEventListener;
    +import android.view.ScaleGestureDetector;
    +import android.view.Surface;
    +import android.view.View;
    +import android.widget.ImageButton;
    +import android.widget.TextView;
    +import android.widget.Toast;
    +
    +import androidx.annotation.NonNull;
    +import androidx.camera.core.Camera;
    +import androidx.camera.core.CameraSelector;
    +import androidx.camera.core.ImageCapture;
    +import androidx.camera.core.ImageCaptureException;
    +import androidx.camera.core.ImageProxy;
    +import androidx.camera.core.Preview;
    +import androidx.camera.core.resolutionselector.AspectRatioStrategy;
    +import androidx.camera.core.resolutionselector.ResolutionSelector;
    +import androidx.camera.lifecycle.ProcessCameraProvider;
    +import androidx.camera.video.FallbackStrategy;
    +import androidx.camera.video.FileOutputOptions;
    +import androidx.camera.video.Quality;
    +import androidx.camera.video.QualitySelector;
    +import androidx.camera.video.Recorder;
    +import androidx.camera.video.Recording;
    +import androidx.camera.video.VideoCapture;
    +import androidx.camera.video.VideoRecordEvent;
    +import androidx.camera.view.PreviewView;
    +import androidx.core.app.ActivityCompat;
    +import androidx.core.content.ContextCompat;
    +
    +import com.google.common.util.concurrent.ListenableFuture;
    +
    +import java.io.File;
    +import java.io.IOException;
    +import java.io.OutputStream;
    +import java.nio.ByteBuffer;
    +import java.text.SimpleDateFormat;
    +import java.util.Date;
    +import java.util.Locale;
    +import java.util.Objects;
    +import java.util.concurrent.ExecutionException;
    +import java.util.concurrent.TimeUnit;
    +
    +import totalcross.AndroidUtils;
    +
    +public class VideoCaptureActivity extends AdjustedInsetsActivity {
    +
    +    public enum VideoQuality {
    +        FHD(1920, 1080), HD(1280, 720), SD(720, 480);
    +
    +        final int width;
    +        final int height;
    +
    +        VideoQuality(int width, int height) {
    +            this.width = width;
    +            this.height = height;
    +        }
    +
    +        private Size getQualitySelectorFor(boolean isLandscape) {
    +            return isLandscape ? new Size(width, height) : new Size(height, width);
    +        }
    +
    +        public static Size getQualitySelectorFor(int width, int height, boolean isLandscape, VideoQuality quality) {
    +            if (width <= 0 || height <= 0) {
    +                return quality.getQualitySelectorFor(isLandscape);
    +            }
    +            return isLandscape ? new Size(width, height) : new Size(height, width);
    +        }
    +    }
    +
    +    public static final String EXTRA_MAX_SECONDS = "max_seconds";
    +    public static final String EXTRA_TARGET_FPS = "target_fps";
    +    public static final String EXTRA_QUALITY = "quality";
    +
    +    private Camera camera;
    +    private ImageButton btnSwitchCamera;
    +    private TextView tvTimer;
    +    private ImageButton btnFlash;
    +
    +    private String lastSavedVideoPath = null;
    +
    +    private static final int REQ_PERMS = 101;
    +    private static final int PLAYBACK_REQ = 201; // new request code for the player
    +    private static final int IMAGE_REQ = 301;
    +
    +    private PreviewView previewView;
    +    private ImageButton btnRecord;
    +    private ImageButton btnBackCapture;
    +
    +    // CameraX
    +    private ProcessCameraProvider cameraProvider;
    +    private VideoCapture videoCapture;
    +    private Recording activeRecording;
    +
    +    // Progress UI
    +    private final Handler progressHandler = new Handler(Looper.getMainLooper());
    +    private long recordingStartTime = 0;
    +    private final int PROGRESS_UPDATE_INTERVAL = 50; // ms
    +    private boolean progressActive = false;
    +
    +    // Params
    +    private int maxSeconds = 30;
    +    private int targetFps = 30;
    +    private VideoQuality qualidade = VideoQuality.HD;
    +
    +    private RecordProgressDrawable progressDrawable;
    +
    +    private int deviceOrientation = Surface.ROTATION_0;
    +
    +    private int width;
    +
    +    private int height;
    +
    +    private int stillQuality;
    +
    +    private int bitrate;
    +
    +    private ImageCapture imageCapture;
    +    private static final long PHOTO_THRESHOLD_MS = 200;
    +
    +    public static final int MODE_VIDEO = 0;
    +    public static final int MODE_PICTURE = 1;
    +    public static final int MODE_FULL = 2;
    +
    +    private int captureMode;
    +
    +    boolean flashEnabled;
    +
    +    boolean isFrontCamera;
    +
    +    CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.activity_video_capture);
    +
    +        tvTimer = findViewById(R.id.tvTimer);
    +        previewView = findViewById(R.id.previewView);
    +        btnRecord = findViewById(R.id.btnCapture);
    +        btnBackCapture = findViewById(R.id.btnBackCapture);
    +        ImageButton btnGallery = findViewById(R.id.btnGallery);
    +        btnGallery.setOnClickListener(v -> {
    +            Intent intent = new Intent(Intent.ACTION_PICK);
    +            intent.setType("image/* video/*");
    +            startActivity(intent);
    +        });
    +
    +        btnFlash = findViewById(R.id.btnFlash);
    +        btnFlash.setOnClickListener(v -> {
    +            if (camera == null) return;
    +
    +            boolean enabled = flashEnabled;
    +            camera.getCameraControl().enableTorch(!enabled);
    +            flashEnabled = !enabled;
    +
    +            btnFlash.setImageResource(
    +                    flashEnabled ? R.drawable.ic_flash_on : R.drawable.ic_flash_off
    +            );
    +        });
    +
    +        btnSwitchCamera = findViewById(R.id.btnSwitchCamera);
    +        btnSwitchCamera.setOnClickListener(v -> {
    +            isFrontCamera = !isFrontCamera;
    +            cameraSelector =
    +                    isFrontCamera
    +                            ? CameraSelector.DEFAULT_FRONT_CAMERA
    +                            : CameraSelector.DEFAULT_BACK_CAMERA;
    +
    +            bindCamera(deviceOrientation);
    +        });
    +
    +        previewView.setScaleType(PreviewView.ScaleType.FILL_CENTER);
    +
    +        Bundle b = getIntent().getExtras();
    +        captureMode = b.getInt("mode");
    +        maxSeconds = Math.max(1, b.getInt(EXTRA_MAX_SECONDS, 30));
    +        targetFps = Math.max(5, b.getInt(EXTRA_TARGET_FPS, 30));
    +        width = b.getInt("width");
    +        height = b.getInt("height");
    +        bitrate = b.getInt("bitrate", 200_000);
    +        stillQuality = b.getInt(EXTRA_QUALITY, 2);
    +        qualidade = (stillQuality == 3 ? VideoQuality.FHD : stillQuality == 1 ? VideoQuality.SD : VideoQuality.HD);
    +
    +        lastSavedVideoPath = b.getString("file");
    +//        lastSavedVideoPath = "/sdcard/DCIM/" + lastSavedVideoPath.substring(lastSavedVideoPath.lastIndexOf('/'));
    +        if (lastSavedVideoPath == null)
    +            lastSavedVideoPath = buildOutputFilePath();
    +
    +        progressDrawable = new RecordProgressDrawable();
    +
    +        btnRecord.setOnTouchListener((v, ev) -> {
    +            if (!permissionsGranted()) {
    +                requestPermissions();
    +                return true;
    +            }
    +            switch (ev.getAction()) {
    +                case MotionEvent.ACTION_DOWN:
    +                    btnSwitchCamera.setEnabled(false);
    +
    +                    if (captureMode != MODE_PICTURE) {
    +                        btnRecord.setImageDrawable(progressDrawable);
    +                        videoCapture.setTargetRotation(deviceOrientation);
    +
    +                        startRecording();
    +                        startProgress();
    +                    }
    +                    return true;
    +
    +                case MotionEvent.ACTION_UP:
    +                case MotionEvent.ACTION_CANCEL:
    +                    if (captureMode == MODE_PICTURE) {
    +                        takePhoto();
    +                    } else {
    +                        stopRecording();
    +                        stopProgress();
    +                    }
    +
    +                    flashEnabled = false;
    +                    btnFlash.setImageResource(R.drawable.ic_flash_off);
    +                    btnSwitchCamera.setEnabled(true);
    +                    return true;
    +            }
    +            return false;
    +        });
    +
    +        btnBackCapture.setOnClickListener(v -> {
    +            setResult(RESULT_CANCELED);
    +            finish();
    +        });
    +
    +        TextView tvHint = findViewById(R.id.tvHint);
    +        tvHint.setVisibility(captureMode == MODE_FULL ? VISIBLE : INVISIBLE);
    +
    +        OrientationEventListener orientationEventListener = new OrientationEventListener(this) {
    +            @Override
    +            public void onOrientationChanged(int orientation) {
    +                if (orientation == ORIENTATION_UNKNOWN) return;
    +
    +                int newOrientation;
    +                if (orientation >= 315 || orientation < 45) {
    +                    newOrientation = Surface.ROTATION_0;      // Portrait
    +                } else if (orientation >= 45 && orientation < 135) {
    +                    newOrientation = Surface.ROTATION_270;     // Landscape LEFT
    +                } else if (orientation >= 135 && orientation < 225) {
    +                    newOrientation = Surface.ROTATION_180;    // Reverse Portrait
    +                } else {
    +                    newOrientation = Surface.ROTATION_90;    // Landscape RIGHT
    +                }
    +
    +                if (newOrientation != deviceOrientation) {
    +                    deviceOrientation = newOrientation;
    +//                    updateCameraConfig should only be called on actual rotation, not when the orientation was just changed
    +//                    updateCameraConfig(deviceOrientation);
    +//                    Log.d("ORIENT", "Orientation changed: " + deviceOrientation);
    +                }
    +            }
    +        };
    +        orientationEventListener.enable();
    +
    +        if (permissionsGranted()) startCamera();
    +        else requestPermissions();
    +    }
    +
    +    // ================================
    +    // PERMISSIONS
    +    // ================================
    +    private boolean permissionsGranted() {
    +        return ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
    +                && ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED;
    +    }
    +
    +    private void requestPermissions() {
    +        ActivityCompat.requestPermissions(this,
    +                new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, REQ_PERMS);
    +    }
    +
    +    @Override
    +    public void onRequestPermissionsResult(int code, @NonNull String[] p, @NonNull int[] r) {
    +        super.onRequestPermissionsResult(code, p, r);
    +        if (code == REQ_PERMS && permissionsGranted()) {
    +            startCamera();
    +        } else {
    +            Toast.makeText(this, "Permissões necessárias", Toast.LENGTH_LONG).show();
    +            finish();
    +        }
    +    }
    +
    +    private void startCamera() {
    +        ListenableFuture future =
    +                ProcessCameraProvider.getInstance(this);
    +
    +        future.addListener(() -> {
    +            try {
    +                cameraProvider = future.get();
    +                bindCamera();
    +            } catch (ExecutionException | InterruptedException ignored) {
    +            }
    +        }, ContextCompat.getMainExecutor(this));
    +    }
    +
    +    private int lastRotation = -1;
    +
    +    private void bindCamera() {
    +        bindCamera(deviceOrientation);
    +    }
    +
    +    private void bindCamera(int rotation) {
    +        lastRotation = rotation;
    +
    +        // Remove all before recreating
    +        cameraProvider.unbindAll();
    +
    +        /*
    +            Preview, VideoCapture and ImageCapture must all be initialized using a compatible
    +            aspect ratio, otherwise the binding may fail. Here, everything is initialized with
    +            a 16:9 aspect ratio using the ResolutionSelector (Preview and ImageCapture) and
    +            the QualitySelector with Quality.HD (VideoCapture).
    +         */
    +        ResolutionSelector resolutionSelector =
    +                new ResolutionSelector.Builder()
    +                        .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY)
    +                        .build();
    +
    +        // PREVIEW
    +        Preview preview = new Preview.Builder()
    +                .setTargetRotation(rotation)
    +                .setResolutionSelector(resolutionSelector)
    +                .build();
    +
    +        preview.setSurfaceProvider(previewView.getSurfaceProvider());
    +
    +        // VIDEO CAPTURE (CameraX VideoCapture)
    +        Recorder recorder =
    +                new Recorder.Builder()
    +                        .setExecutor(ContextCompat.getMainExecutor(this))
    +                        .setQualitySelector(QualitySelector.from(Quality.HD,
    +                                FallbackStrategy.higherQualityOrLowerThan(Quality.HD)))
    +                        .setTargetVideoEncodingBitRate(bitrate)
    +                        .build();
    +
    +        videoCapture = VideoCapture.withOutput(recorder);
    +
    +        final boolean isLandscape =
    +                getResources().getConfiguration().screenWidthDp >
    +                        getResources().getConfiguration().screenHeightDp;
    +
    +        imageCapture = new ImageCapture.Builder()
    +                .setTargetRotation(rotation)
    +                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
    +                .setJpegQuality(stillQuality == 1 ? 75 : stillQuality == 2 ? 85 : 100)
    +                /*
    +                    We'll keep using setTargetResolution for the time being, because the preferred
    +                    method using ResolutionSelector, as of now, only allows selecting 4:3 or 16:9
    +                    resolutions.
    +                 */
    +                .setTargetResolution(
    +                        /*
    +                            setTargetResolution picks resolution based on the current orientation
    +                            of the application, so in landscape we have to invert the dimensions to
    +                            pick the correct resolution.
    +                         */
    +                        isLandscape ?
    +                                new Size(width, height) :
    +                                new Size(height, width))
    +                .build();
    +
    +        // Final bind
    +        camera = cameraProvider.bindToLifecycle(
    +                this,
    +                cameraSelector,
    +                preview,
    +                videoCapture,
    +                imageCapture
    +        );
    +
    +        enablePinchToZoom(this, camera, previewView);
    +    }
    +
    +    private void startRecording() {
    +        if (videoCapture == null) {
    +            return;
    +        }
    +
    +        File file = new File(lastSavedVideoPath);
    +        FileOutputOptions outputOptions =
    +                new FileOutputOptions.Builder(file).build();
    +
    +        activeRecording =
    +                videoCapture.getOutput()
    +                        .prepareRecording(this, outputOptions)
    +                        .withAudioEnabled()
    +                        .start(ContextCompat.getMainExecutor(this),
    +                                event -> {
    +                                    if (event instanceof VideoRecordEvent.Status) {
    +                                        VideoRecordEvent.Status s = (VideoRecordEvent.Status) event;
    +
    +                                        long seconds =
    +                                                TimeUnit.NANOSECONDS.toSeconds(
    +                                                        s.getRecordingStats().getRecordedDurationNanos()
    +                                                );
    +
    +                                        updateTimer(seconds);
    +                                        tvTimer.setVisibility(VISIBLE);
    +                                        tvTimer.setCompoundDrawablesWithIntrinsicBounds(
    +                                                R.drawable.ic_rec_dot, 0, 0, 0
    +                                        );
    +                                    }
    +                                    if (event instanceof VideoRecordEvent.Finalize) {
    +                                        tvTimer.setVisibility(View.GONE);
    +                                        VideoRecordEvent.Finalize finalize =
    +                                                (VideoRecordEvent.Finalize) event;
    +
    +                                        if (!finalize.hasError()) {
    +                                            openVideoPlayer(lastSavedVideoPath);
    +                                        } else {
    +                                            if (TimeUnit.NANOSECONDS.toMillis(
    +                                                    finalize.getRecordingStats().getRecordedDurationNanos()
    +                                            ) < PHOTO_THRESHOLD_MS) {
    +                                                // it's fine
    +                                                if (captureMode == MODE_FULL) {
    +                                                    takePhoto();
    +                                                }
    +                                            } else {
    +                                                file.delete();
    +                                                Toast.makeText(
    +                                                        this,
    +                                                        "Record failed: " + ((VideoRecordEvent.Finalize) event).getError(),
    +                                                        Toast.LENGTH_LONG
    +                                                ).show();
    +                                            }
    +                                        }
    +                                    }
    +                                });
    +    }
    +
    +
    +    private void stopRecording() {
    +        if (activeRecording != null) {
    +            activeRecording.stop();
    +            activeRecording = null;
    +        }
    +    }
    +
    +    private void startProgress() {
    +        progressActive = true;
    +        recordingStartTime = System.currentTimeMillis();
    +        progressDrawable.setProgress(0f);
    +        progressDrawable.setProgressColor(Color.RED);
    +        progressHandler.post(progressRunnable);
    +    }
    +
    +    private void stopProgress() {
    +        progressActive = false;
    +        progressDrawable.setProgress(0f);
    +        btnRecord.setImageResource(R.drawable.bg_record_button_small);
    +        progressHandler.removeCallbacks(progressRunnable);
    +    }
    +
    +    private final Runnable progressRunnable = new Runnable() {
    +        @Override
    +        public void run() {
    +            if (!progressActive) {
    +                return;
    +            }
    +            long elapsed = System.currentTimeMillis() - recordingStartTime;
    +            float p = Math.min(1f, (float) elapsed / (maxSeconds * 1000));
    +            progressDrawable.setProgress(p);
    +            btnRecord.invalidate();
    +
    +            if (p >= 1f) {
    +                stopRecording();
    +                stopProgress();
    +            } else {
    +                progressHandler.postDelayed(this, PROGRESS_UPDATE_INTERVAL);
    +            }
    +        }
    +    };
    +
    +    private void updateTimer(long seconds) {
    +        long min = seconds / 60;
    +        long sec = seconds % 60;
    +
    +        String text = String.format(Locale.US, "%02d:%02d", min, sec);
    +        tvTimer.setText(text);
    +    }
    +
    +    private void takePhoto() {
    +        if (imageCapture == null) return;
    +
    +//        File dir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    +//        if (dir != null && !dir.exists()) dir.mkdirs();
    +//
    +//        String ts = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
    +//        File photoFile = new File(dir, "IMG_" + ts + ".jpg");
    +        File photoFile = new File(lastSavedVideoPath);
    +
    +        ImageCapture.OutputFileOptions options =
    +                new ImageCapture.OutputFileOptions.Builder(photoFile).build();
    +
    +        imageCapture.takePicture(
    +                ContextCompat.getMainExecutor(this),
    +                new ImageCapture.OnImageCapturedCallback() {
    +                    @Override
    +                    public void onCaptureSuccess(@NonNull ImageProxy image) {
    +                        int rotation = image.getImageInfo().getRotationDegrees();
    +
    +                        // 1. Convert ImageProxy to Bitmap
    +                        Bitmap bmp = imageProxyToBitmap(image);
    +
    +                        // 2. Adjust rotation
    +                        bmp = rotateBitmap(bmp, rotation);
    +
    +                        try {
    +                            OutputStream outStream = getContentResolver().openOutputStream(Uri.fromFile(photoFile));
    +                            saveBitmap(bmp, outStream);
    +                            openImagePreview(photoFile.getAbsolutePath());
    +                        } catch (IOException e) {
    +                            AndroidUtils.handleException(e, false);
    +                        }
    +
    +                        image.close();
    +                    }
    +
    +                    @Override
    +                    public void onError(@NonNull ImageCaptureException exception) {
    +                        AndroidUtils.handleException(exception, false);
    +                        setResult(RESULT_CANCELED);
    +                        finish();
    +                    }
    +                });
    +    }
    +
    +    private Bitmap imageProxyToBitmap(ImageProxy image) {
    +        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
    +        byte[] bytes = new byte[buffer.remaining()];
    +        buffer.get(bytes);
    +        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    +    }
    +
    +    private Bitmap rotateBitmap(Bitmap bitmap, int rotationDegrees) {
    +        Matrix matrix = new Matrix();
    +        matrix.postRotate(rotationDegrees);
    +        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    +    }
    +
    +    private void saveBitmap(Bitmap bitmap, OutputStream outStream) throws IOException {
    +        bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outStream);
    +        outStream.flush();
    +        outStream.close();
    +    }
    +
    +    private void openVideoPlayer(String path) {
    +        Intent videoIntent = new Intent(this, VideoPlayerActivity.class);
    +        videoIntent.putExtra("video_path", path);
    +        videoIntent.putExtra("playback_only", false);
    +        // start playback activity - restore capture upon return
    +        startActivityForResult(videoIntent, PLAYBACK_REQ);
    +    }
    +
    +    private void openImagePreview(String path) {
    +        Intent i = new Intent(this, ImagePreviewActivity.class);
    +        i.putExtra("image_path", path);
    +        startActivityForResult(i, IMAGE_REQ);
    +    }
    +
    +    private String buildOutputFilePath() {
    +        File dir = getExternalFilesDir(Environment.DIRECTORY_MOVIES);
    +        if (dir != null && !dir.exists()) {
    +            dir.mkdirs();
    +        }
    +        String ts = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
    +        return new File(dir, "VID_" + ts + ".mp4").getAbsolutePath();
    +    }
    +
    +    @Override
    +    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    +        super.onActivityResult(requestCode, resultCode, data);
    +        if (requestCode == PLAYBACK_REQ || requestCode == IMAGE_REQ) {
    +            if (resultCode == RESULT_OK) {
    +                setResult(RESULT_OK);
    +                finish();
    +            } else {
    +                startCamera();
    +            }
    +        }
    +    }
    +
    +    /* =========================
    +   🔍 PINCH TO ZOOM
    +   ========================= */
    +    private void enablePinchToZoom(Context context, Camera camera, PreviewView previewView) {
    +        ScaleGestureDetector detector =
    +                new ScaleGestureDetector(context,
    +                        new ScaleGestureDetector.SimpleOnScaleGestureListener() {
    +                            @Override
    +                            public boolean onScale(@NonNull ScaleGestureDetector d) {
    +                                float scale =
    +                                        Objects.requireNonNull(camera.getCameraInfo().getZoomState()
    +                                                .getValue()).getZoomRatio() * d.getScaleFactor();
    +                                camera.getCameraControl().setZoomRatio(scale);
    +                                return true;
    +                            }
    +                        });
    +
    +        previewView.setOnTouchListener((v, e) -> {
    +            detector.onTouchEvent(e);
    +            return true;
    +        });
    +    }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/VideoPlayerActivity.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/VideoPlayerActivity.java
    new file mode 100644
    index 0000000000..490a3131c3
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/VideoPlayerActivity.java
    @@ -0,0 +1,155 @@
    +package totalcross.android;
    +
    +import android.net.Uri;
    +import android.os.Bundle;
    +import android.os.Handler;
    +import android.os.Looper;
    +import android.view.View;
    +import android.widget.ImageButton;
    +import android.widget.SeekBar;
    +import android.widget.Toast;
    +import android.widget.VideoView;
    +
    +import java.io.File;
    +
    +public class VideoPlayerActivity extends AdjustedInsetsActivity {
    +
    +    private VideoView videoView;
    +    private ImageButton btnPlayPause, btnAvancar, btnRetroceder, btnSalvar, btnBackPlayer;
    +    private SeekBar seekBar;
    +    private Handler handler = new Handler(Looper.getMainLooper());
    +    private boolean isSeeking = false;
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.activity_video_player);
    +
    +        videoView = findViewById(R.id.videoView);
    +        btnPlayPause = findViewById(R.id.btnPlayPause);
    +        btnAvancar = findViewById(R.id.btnForward);
    +        btnRetroceder = findViewById(R.id.btnRewind);
    +        seekBar = findViewById(R.id.seekBar);
    +        btnSalvar = findViewById(R.id.btnSalvar);
    +        btnBackPlayer = findViewById(R.id.btnBackPlayer);
    +
    +        // caminho do vídeo recebido pela intent
    +        String videoPath = getIntent().getStringExtra("video_path");
    +        if (videoPath == null) {
    +            Toast.makeText(this, "File not found!", Toast.LENGTH_SHORT).show();
    +            finish();
    +            return;
    +        }
    +
    +        boolean playbackOnly = getIntent().getBooleanExtra("playback_only", true);
    +        if (playbackOnly) {
    +            btnSalvar.setVisibility(View.GONE);
    +        }
    +
    +        // Toast.makeText(this, "File1 " + videoPath, Toast.LENGTH_SHORT).show();
    +        File videoFile = new File(videoPath);
    +
    +        // Toast.makeText(this, "File2 " + videoFile, Toast.LENGTH_SHORT).show();
    +        Uri videoUri = Uri.fromFile(videoFile);
    +
    +        // Toast.makeText(this, "File3 " + videoUri, Toast.LENGTH_SHORT).show();
    +        videoView.setVideoURI(videoUri);
    +
    +        videoView.setOnPreparedListener(mp -> {
    +            seekBar.setMax(videoView.getDuration());
    +            videoView.start();
    +            btnPlayPause.setImageResource(R.drawable.ic_pause_new);
    +            updateSeekBar();
    +        });
    +
    +        btnPlayPause.setOnClickListener(v -> togglePlayPause());
    +        btnAvancar.setOnClickListener(v -> seekBy(1000));
    +        btnRetroceder.setOnClickListener(v -> seekBy(-1000));
    +
    +        /**
    +         *
    +         * */
    +        btnSalvar.setOnClickListener(v -> {
    +            setResult(RESULT_OK);
    +            finish();
    +
    +            // Toast.makeText(this, "Vídeo salvo em: " + videoPath, Toast.LENGTH_LONG).show();
    +            // MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
    +            // try {
    +            //     mediaMetadataRetriever.setDataSource(videoPath);
    +            //     File file = new File("/sdcard/DCIM/", videoFile.getName());
    +            //     if (file.exists()) {
    +            //         Toast.makeText(this, "Vídeo já existe na galeria.", Toast.LENGTH_SHORT).show();
    +            //     } else {
    +            //         FileInputStream in = new FileInputStream(videoFile);
    +            //         FileOutputStream out = new FileOutputStream(file);
    +            //         byte[] buf = new byte[1024];
    +            //         int len;
    +            //         while ((len = in.read(buf)) > 0) {
    +            //             out.write(buf, 0, len);
    +            //         }
    +            //         in.close();
    +            //         out.close();
    +            //         Toast.makeText(this, "Vídeo salvo na galeria: " + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
    +            //     }
    +            // } catch (Exception ignored) {
    +            // } finally {
    +            //     try { mediaMetadataRetriever.release(); } catch (Exception ignored) {}
    +            // }
    +        });
    +
    +        btnBackPlayer.setOnClickListener(v -> {
    +            setResult(RESULT_CANCELED);
    +            finish();
    +        });
    +
    +        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    +            @Override
    +            public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) {
    +                if (fromUser && videoView != null) videoView.seekTo(progress);
    +            }
    +
    +            @Override
    +            public void onStartTrackingTouch(SeekBar sb) {
    +                isSeeking = true;
    +            }
    +
    +            @Override
    +            public void onStopTrackingTouch(SeekBar sb) {
    +                isSeeking = false;
    +            }
    +        });
    +
    +        videoView.setOnCompletionListener(mp -> btnPlayPause.setImageResource(R.drawable.ic_play_new));
    +    }
    +
    +    private void togglePlayPause() {
    +        if (videoView.isPlaying()) {
    +            videoView.pause();
    +            btnPlayPause.setImageResource(R.drawable.ic_play_new);
    +        } else {
    +            videoView.start();
    +            btnPlayPause.setImageResource(R.drawable.ic_pause_new);
    +        }
    +    }
    +
    +    private void seekBy(int ms) {
    +        int pos = videoView.getCurrentPosition() + ms;
    +        pos = Math.max(0, Math.min(pos, videoView.getDuration()));
    +        videoView.seekTo(pos);
    +    }
    +
    +    private void updateSeekBar() {
    +        if (videoView != null && videoView.isPlaying() && !isSeeking) {
    +            seekBar.setProgress(videoView.getCurrentPosition());
    +        }
    +        handler.postDelayed(this::updateSeekBar, 500);
    +    }
    +
    +    @Override
    +    protected void onPause() {
    +        super.onPause();
    +        handler.removeCallbacksAndMessages(null);
    +        if (videoView.isPlaying()) videoView.pause();
    +    }
    +}
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseMessageReceiver.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseMessageReceiver.java
    index 1052a9b06e..e9bd373e6c 100755
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseMessageReceiver.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseMessageReceiver.java
    @@ -29,9 +29,9 @@
     import android.graphics.Color;
     import android.os.Build;
     import android.app.NotificationChannel;
    -import android.support.annotation.RequiresApi;
    -import android.support.v4.app.NotificationCompat;
    -import android.support.v4.app.NotificationCompat.Builder;
    +import androidx.annotation.RequiresApi;
    +import androidx.core.app.NotificationCompat;
    +import androidx.core.app.NotificationCompat.Builder;
     
     /**
      * Mudanças baseadas em https://developers.google.com/cloud-messaging/android/android-migrate-fcm
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseUtils.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseUtils.java
    index 84f8de53cd..09f27483e2 100644
    --- a/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseUtils.java
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/firebase/FirebaseUtils.java
    @@ -5,15 +5,13 @@
     import java.io.FileOutputStream;
     import java.io.IOException;
     
    -import android.content.BroadcastReceiver;
     import android.content.Context;
     import android.content.Intent;
    -import android.content.IntentFilter;
    -import android.support.v4.content.LocalBroadcastManager;
    +
    +import androidx.localbroadcastmanager.content.LocalBroadcastManager;
     import com.google.firebase.FirebaseApp;
     import com.google.firebase.iid.FirebaseInstanceId;
     import totalcross.AndroidUtils;
    -import totalcross.Launcher4A;
     
     public class FirebaseUtils
     {
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/HoneywellScanner.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/HoneywellScanner.java
    new file mode 100644
    index 0000000000..6dfec3dbc6
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/HoneywellScanner.java
    @@ -0,0 +1,137 @@
    +/*********************************************************************************
    + * TotalCross Software Development Kit * Copyright (C) 2000-2012 SuperWaba Ltda. * All Rights Reserved * * This library
    + * and virtual machine is distributed in the hope that it will * be useful, but WITHOUT ANY WARRANTY; without even the
    + * implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * This file is covered by the GNU LESSER
    + * GENERAL PUBLIC LICENSE VERSION 3.0 * A copy of this license is located in file license.txt at the root of this * SDK
    + * or can be downloaded here: * http://www.gnu.org/licenses/lgpl-3.0.txt * *
    + *********************************************************************************/
    +
    +package totalcross.android.scanners;
    +
    +import totalcross.*;
    +
    +import android.view.*;
    +import com.honeywell.aidc.*;
    +import com.honeywell.aidc.AidcManager.*;
    +import java.util.*;
    +
    +/**
    + * Scanner class for Android.
    + */
    +public class HoneywellScanner implements IScanner, BarcodeReader.BarcodeListener, CreatedCallback
    +{
    +   /** Starts the definition of the barcode parameters. */
    +   public static final String START_BATCH = "***START_BATCH***";
    +   /** Ends the definition of the barcode parameters. */
    +   public static final String END_BATCH = "***END_BATCH***";
    +
    +   private static Map properties;
    +   private static BarcodeReader barcodeReader;
    +   private static AidcManager manager;
    +   private static String barcode;
    +
    +   @Override
    +   public boolean scannerActivate()
    +   {
    +      if (barcodeReader == null)
    +      {
    +         // get bar code instance from MainActivity
    +         AidcManager.create(Launcher4A.loader, this);
    +         long timeout = System.currentTimeMillis() + 20*1000;
    +         while (barcodeReader == null && System.currentTimeMillis() < timeout)
    +            try {Thread.sleep(40);} catch (Exception e) {}
    +         if (barcodeReader != null)
    +            try
    +            {
    +               barcodeReader.addBarcodeListener(this);
    +               barcodeReader.setProperty(BarcodeReader.PROPERTY_TRIGGER_CONTROL_MODE, BarcodeReader.TRIGGER_CONTROL_MODE_AUTO_CONTROL);
    +               barcodeReader.claim(); // required to start notifications!
    +            }
    +            catch (Exception e) {AndroidUtils.handleException(e,false);}
    +      }
    +      return barcodeReader != null;
    +   }
    +
    +   @Override
    +   public void onCreated(AidcManager aidcManager) 
    +   {
    +      manager = aidcManager;
    +      barcodeReader = manager.createBarcodeReader();
    +   }
    +
    +   @Override
    +   public void onBarcodeEvent(final BarcodeReadEvent event)
    +   {
    +      barcode = event.getBarcodeData();
    +      Launcher4A.instance._postEvent(Launcher4A.BARCODE_READ, 0, 0, 0, 0, 0);
    +   }
    +
    +   @Override
    +   public void onFailureEvent(BarcodeFailureEvent event)
    +   {
    +   }
    +
    +   public boolean setBarcodeParam(int barcodeType, boolean enable)
    +   {
    +      return true;
    +   }
    +
    +   public String getData()
    +   {
    +      String b = barcode;
    +      barcode = "";
    +      return b;
    +   }
    +
    +   public boolean deactivate()
    +   {
    +      if (barcodeReader != null)
    +      {
    +         barcodeReader.removeBarcodeListener(this);
    +         barcodeReader.release();
    +         barcodeReader.close();
    +         barcodeReader = null;
    +      }
    +      manager.close();
    +      manager = null;
    +      return true;
    +   }
    +
    +   public boolean checkScanner(KeyEvent event)
    +   {
    +      return false;
    +   }
    +
    +   public void setParam(String what, String value)
    +   {
    +      if (what.equals(START_BATCH))
    +         properties = new HashMap();
    +      else
    +      if (barcodeReader == null)
    +         ;
    +      else
    +      if (what.equals(END_BATCH))
    +      {
    +         barcodeReader.setProperties(properties);
    +         AndroidUtils.debug("Scanner parameters: "+properties);
    +         properties = null;
    +      }
    +      else
    +      {
    +         if (value.equalsIgnoreCase("true"))
    +            properties.put(what, true);
    +         else
    +         if (value.equalsIgnoreCase("false"))
    +            properties.put(what, false);
    +         else
    +            try
    +            {
    +               properties.put(what, Integer.parseInt(value)); // try as integer
    +            }
    +            catch (Exception e)
    +            {
    +               properties.put(what, value);
    +            }
    +      }
    +   }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/IntermecScanner.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/IntermecScanner.java
    new file mode 100644
    index 0000000000..4c09ad0acd
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/IntermecScanner.java
    @@ -0,0 +1,357 @@
    +/*********************************************************************************
    + *  TotalCross Software Development Kit                                          *
    + *  Copyright (C) 2000-2012 SuperWaba Ltda.                                      *
    + *  All Rights Reserved                                                          *
    + *                                                                               *
    + *  This library and virtual machine is distributed in the hope that it will     *
    + *  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    *
    + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                         *
    + *                                                                               *
    + *  This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0    *
    + *  A copy of this license is located in file license.txt at the root of this    *
    + *  SDK or can be downloaded here:                                               *
    + *  http://www.gnu.org/licenses/lgpl-3.0.txt                                     *
    + *                                                                               *
    + *********************************************************************************/
    +
    +package totalcross.android.scanners;
    +
    +import android.view.*;
    +import com.intermec.aidc.*;
    +import java.util.concurrent.*;
    +
    +import totalcross.*;
    +import totalcross.android.*;
    +
    +/**
    + * Scanner class for Android.
    + */
    +public class IntermecScanner implements IScanner
    +{ 
    +   private final static int INTERMEC_AUSTRALIAN_POST = 1;
    +   private final static int INTERMEC_AZTEC = 2;
    +   private final static int INTERMEC_BPO = 3;
    +   private final static int INTERMEC_CANADA_POST = 4;
    +   private final static int INTERMEC_CODABAR = 5;
    +   private final static int INTERMEC_CODABLOCK_A = 6;
    +   private final static int INTERMEC_CODABLOCK_F = 7;
    +   private final static int INTERMEC_CODE_11 = 8;
    +   private final static int INTERMEC_CODE_128 = 9;
    +   private final static int INTERMEC_CODE_GS1_128 = 10;
    +   private final static int INTERMEC_CODE_ISBT_128 = 11;
    +   private final static int INTERMEC_CODE_39 = 12;
    +   private final static int INTERMEC_CODE_93 = 13;
    +   private final static int INTERMEC_DATA_MATRIX = 14;
    +   private final static int INTERMEC_DUTCH_POST = 15;
    +   private final static int INTERMEC_EAN_UPC_EAN_13 = 16;
    +   private final static int INTERMEC_EAN_UPC_EAN_8 = 17;
    +   private final static int INTERMEC_EAN_UPC_UPCA = 18;
    +   private final static int INTERMEC_EAN_UPC_UPC_E = 19;
    +   private final static int INTERMEC_EAN_UPC_UPC_E1 = 20;
    +   private final static int INTERMEC_GS1_COMPOSITE = 21;
    +   private final static int INTERMEC_GS1_COMPOSITE_C = 22;
    +   private final static int INTERMEC_GS1_DATA_BAR_EXPANDED = 23;            
    +   private final static int INTERMEC_GS1_DATA_BAR_LIMITED = 24;            
    +   private final static int INTERMEC_GS1_OMINI_DIRECTIONAL = 25;            
    +   private final static int INTERMEC_HAN_XIN = 26;            
    +   private final static int INTERMEC_INFOMAIL = 27;
    +   private final static int INTERMEC_INTELLIGENT_MAIL = 28;
    +   private final static int INTERMEC_INTERLEAVED_2_OF_5 = 29;
    +   private final static int INTERMEC_JAPAN_POST = 30;
    +   private final static int INTERMEC_MATRIX_2_OF_5 = 31;
    +   private final static int INTERMEC_MAXICODE = 32;
    +   private final static int INTERMEC_MICRO_PDF_417 = 33;
    +   private final static int INTERMEC_MSI = 34;
    +   private final static int INTERMEC_PDF_417 = 35;
    +   private final static int INTERMEC_PLANET = 36;
    +   private final static int INTERMEC_PLESSEY = 37;
    +   private final static int INTERMEC_POSTNET = 38;
    +   private final static int INTERMEC_QR_CODE = 39;
    +   private final static int INTERMEC_STANDARD_2_OF_5 = 40;
    +   private final static int INTERMEC_SWEDEN_POST = 41;
    +   private final static int INTERMEC_TELEPEN = 42;
    +   private final static int INTERMEC_TLC_39 = 43;
    +   
    +   private BarcodeReader bcr;
    +   private VirtualWedge wedg;
    +   private boolean isOk;
    +   private Semaphore semaphore = new Semaphore(1);
    +   
    +   public boolean scannerActivate()
    +   {
    +      // Make sure the BarcodeReader dependent service is connected and
    +      // register a callback to service connect and disconnect events.
    +      AidcManager.connectService(Launcher4A.loader, new AidcManager.IServiceListener() 
    +      {
    +         public void onConnect()
    +         {  		    
    +			   try
    +            {
    +			      semaphore.acquire();				   
    +		      }		           
    +            catch (InterruptedException exception) {}
    +			   attachBarcodeReader(); // The dependent service is connected and it is ready to receive barcode requests.
    +			   semaphore.release();				
    +		   }
    +
    +         public void onDisconnect() {}
    +
    +      });
    +      return isOk;
    +   }
    +   
    +   public boolean setBarcodeParam(int barcodeType, boolean enable)
    +   {
    +      if (bcr == null)
    +         return false;
    +      
    +      try
    +	   {	
    +         semaphore.acquire();	
    +	   }
    +	   catch (InterruptedException exception) {}
    +		switch (barcodeType)
    +		{
    +         case INTERMEC_AUSTRALIAN_POST:
    +            bcr.symbology.australianPost.setEnable(enable);
    +            break;
    +         case INTERMEC_AZTEC:
    +            bcr.symbology.aztec.setEnable(enable);
    +            break;
    +         case INTERMEC_BPO:
    +            bcr.symbology.bpo.setEnable(enable);
    +            break;
    +         case INTERMEC_CANADA_POST:
    +            bcr.symbology.canadaPost.setEnable(enable);
    +            break;
    +         case INTERMEC_CODABAR:
    +            bcr.symbology.codabar.setEnable(enable);
    +            break;
    +         case INTERMEC_CODABLOCK_A:
    +            bcr.symbology.codablockA.setEnable(enable);
    +            break;
    +         case INTERMEC_CODABLOCK_F:
    +            bcr.symbology.codablockF.setEnable(enable);
    +            break;
    +         case INTERMEC_CODE_11:
    +            bcr.symbology.code11.setEnable(enable);
    +            break;
    +         case INTERMEC_CODE_128:
    +            bcr.symbology.code128.setEnable(enable);
    +            break;        
    +         case INTERMEC_CODE_GS1_128:
    +            bcr.symbology.code128.setGS1_128Enable(enable);
    +            break;
    +         case INTERMEC_CODE_ISBT_128:
    +            bcr.symbology.code128.setISBT128Enable(enable);
    +            break;
    +         case INTERMEC_CODE_39:    
    +            bcr.symbology.code39.setEnable(enable);
    +            break;   
    +         case INTERMEC_CODE_93:
    +            bcr.symbology.code93.setEnable(enable);
    +            break;   
    +         case INTERMEC_DATA_MATRIX:
    +            bcr.symbology.datamatrix.setEnable(enable);
    +            break;
    +         case INTERMEC_DUTCH_POST:
    +            bcr.symbology.dutchPost.setEnable(enable);
    +            break;
    +         case INTERMEC_EAN_UPC_EAN_13:
    +            bcr.symbology.eanUpc.setEan13Enable(enable);
    +            break;
    +         case INTERMEC_EAN_UPC_EAN_8:
    +            bcr.symbology.eanUpc.setEan8Enable(enable);
    +            break;
    +         case INTERMEC_EAN_UPC_UPCA:
    +            bcr.symbology.eanUpc.setUPCAEnable(enable);
    +            break;
    +         case INTERMEC_EAN_UPC_UPC_E:
    +            bcr.symbology.eanUpc.setUPCEEnable(enable);
    +            break;
    +         case INTERMEC_EAN_UPC_UPC_E1:
    +            bcr.symbology.eanUpc.setUPCE1Enable(enable);
    +            break;            
    +         case INTERMEC_GS1_COMPOSITE:       
    +            bcr.symbology.gs1Composite.setEnable(enable);
    +            break;      
    +         case INTERMEC_GS1_COMPOSITE_C:
    +            bcr.symbology.gs1Composite.setGS1CompositeCEnable(enable);
    +            break;
    +         case INTERMEC_GS1_DATA_BAR_EXPANDED:       
    +            bcr.symbology.gs1DataBarExpanded.setEnable(enable);
    +            break;   
    +         case INTERMEC_GS1_DATA_BAR_LIMITED:    
    +            bcr.symbology.gs1DataBarLimited.setEnable(enable);
    +            break;   
    +         case INTERMEC_GS1_OMINI_DIRECTIONAL:       
    +            bcr.symbology.gs1DataBarOmniDirectional.setEnable(enable);
    +            break;   
    +         case INTERMEC_HAN_XIN:    
    +            bcr.symbology.hanXin.setEnable(enable);
    +            break;
    +         case INTERMEC_INFOMAIL:    
    +            bcr.symbology.infomail.setEnable(enable);
    +            break;
    +         case INTERMEC_INTELLIGENT_MAIL:    
    +            bcr.symbology.intelligentMail.setEnable(enable);
    +            break;
    +         case INTERMEC_INTERLEAVED_2_OF_5:    
    +            bcr.symbology.interleaved2Of5.setEnable(enable);
    +            break;
    +         case INTERMEC_JAPAN_POST:       
    +            bcr.symbology.japanPost.setEnable(enable);
    +            break;
    +         case INTERMEC_MATRIX_2_OF_5:       
    +            bcr.symbology.matrix2Of5.setEnable(enable);
    +            break;
    +         case INTERMEC_MAXICODE:       
    +            bcr.symbology.maxicode.setEnable(enable);
    +            break;
    +         case INTERMEC_MICRO_PDF_417:       
    +            bcr.symbology.microPdf417.setEnable(enable);
    +            break;
    +         case INTERMEC_MSI:       
    +            bcr.symbology.msi.setEnable(enable);
    +            break;
    +         case INTERMEC_PDF_417:    
    +            bcr.symbology.pdf417.setEnable(enable);
    +            break;
    +         case INTERMEC_PLANET:       
    +            bcr.symbology.planet.setEnable(enable);
    +            break;
    +         case INTERMEC_PLESSEY:       
    +            bcr.symbology.plessey.setEnable(enable);
    +            break;
    +         case INTERMEC_POSTNET:    
    +            bcr.symbology.postnet.setEnable(enable);
    +            break;
    +         case INTERMEC_QR_CODE: 
    +            bcr.symbology.qrCode.setEnable(enable);
    +            break;
    +         case INTERMEC_STANDARD_2_OF_5:    
    +            bcr.symbology.standard2Of5.setEnable(enable);
    +            break;
    +         case INTERMEC_SWEDEN_POST:       
    +            bcr.symbology.swedenPost.setEnable(enable);
    +            break;
    +         case INTERMEC_TELEPEN:
    +            bcr.symbology.telepen.setEnable(enable);
    +            break;
    +         case INTERMEC_TLC_39:
    +            bcr.symbology.tlc39.setEnable(enable);
    +            break;
    +      }
    +      semaphore.release();
    +      return true;
    +   }
    +   
    +   public String getData()
    +   {
    +      String strBarcodeData;
    +	   Semaphore loaderSemaphore = Loader.semaphore;
    +	  
    +	   try
    +	   {
    +         loaderSemaphore.acquire();		 
    +      }
    +	   catch (InterruptedException exception) {}
    +      strBarcodeData = Launcher4A.loader.strBarcodeData;
    +	   loaderSemaphore.release();
    +	  
    +	   return strBarcodeData;
    +   }
    +   
    +   public boolean deactivate()
    +   {
    +      if (bcr == null) return true;
    +      boolean ret = true;
    +      
    +      try
    +      { 
    +         if (bcr != null)
    +         {           	 
    +             bcr.removeBarcodeReadListener(Launcher4A.loader);             
    +             bcr.setScannerEnable(false);
    +             bcr.close();
    +         }
    +      }
    +	   catch (Exception exception)
    +      {
    +         String message = exception.getMessage();
    +         AndroidUtils.debug(message != null? message : "Could not deactivate scanner.");     
    +         ret = false;		 
    +      }
    +      
    +      try
    +      {	  
    +         if (wedg != null)
    +         {
    +            wedg.setEnable(true);
    +            wedg = null;
    +         }
    +	   }
    +      catch (Exception exception)
    +      {
    +         String message = exception.getMessage();
    +         AndroidUtils.debug(message != null? message : "Could not deactivate scanner.");        
    +         ret = false;   
    +	   }        
    +         
    +      try
    +		{
    +		   AidcManager.disconnectService(); //disconnect from data collection service
    +      }
    +		catch (Exception exception) 
    +		{
    +		   String message = exception.getMessage();
    +         AndroidUtils.debug(message != null? message : "Could not deactivate scanner.");
    +         ret = false; 
    +		}
    +		
    +      bcr = null;
    +      return ret;  
    +   }
    +   
    +   private void attachBarcodeReader()
    +   {
    +      isOk = true;
    +      
    +      try
    +      {             
    +		   if ((wedg = new VirtualWedge()).isEnabled())
    +            wedg.setEnable(false); // disable virtual wedge  
    +	   }
    +      catch (Exception exception)
    +      {
    +	      String message = exception.getMessage();
    +         AndroidUtils.debug(message != null? message : "Could not activate scanner.");		 
    +		   wedg = null;
    +		   isOk = false;
    +      }
    +      
    +      try
    +      {                        
    +         if (!(bcr = new BarcodeReader()).isScannerEnabled())
    +		      bcr.setScannerEnable(true); // set barcode reader object for internal scanner
    +        
    +         bcr.addBarcodeReadListener(Launcher4A.loader); // add barcode reader listener
    +      }
    +      catch (Exception exception)
    +      {
    +	      String message = exception.getMessage();
    +         AndroidUtils.debug(message != null? message : "Could not activate scanner.");
    +		   bcr = null;
    +		   isOk = false;
    +      }
    +   }
    +
    +   public boolean checkScanner(KeyEvent event)
    +   {
    +      return false;
    +   }
    +
    +   public void setParam(String what, String value)
    +   {
    +   }
    +}
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/MotorolaScanner.java b/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/MotorolaScanner.java
    new file mode 100644
    index 0000000000..15c4d9f18b
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/java/totalcross/android/scanners/MotorolaScanner.java
    @@ -0,0 +1,106 @@
    +package totalcross.android.scanners;
    +
    +import android.content.*;
    +import android.view.*;
    +
    +import totalcross.*;
    +import com.symbol.emdk.*;
    +import com.symbol.emdk.barcode.*;
    +import java.util.ArrayList;
    +
    +public class MotorolaScanner implements IScanner {
    +  private boolean isActive;
    +  private String barcode;
    +  private Scanner scanner;
    +
    +  @Override
    +  public boolean scannerActivate() {
    +    EMDKResults results = EMDKManager.getEMDKManager(Launcher4A.loader.getApplicationContext(),
    +        new EMDKManager.EMDKListener() {
    +
    +          @Override
    +          public void onOpened(EMDKManager emdkManager) {
    +            BarcodeManager barcodeManager = (BarcodeManager) emdkManager.getInstance(EMDKManager.FEATURE_TYPE.BARCODE);
    +            scanner = barcodeManager.getDevice(BarcodeManager.DeviceIdentifier.DEFAULT);
    +
    +            if (!scanner.isEnabled()) {
    +              try {
    +                scanner.enable();
    +              } catch (Exception e) {
    +                e.printStackTrace();
    +              }
    +            }
    +
    +            scanner.addStatusListener(new Scanner.StatusListener() {
    +
    +              @Override
    +              public void onStatus(StatusData statusData) {
    +                if (statusData.getState() == StatusData.ScannerStates.IDLE) {
    +                  try {
    +                    scanner.read();
    +                  } catch (Exception e) {
    +                    e.printStackTrace();
    +                  }
    +                }
    +              }
    +            });
    +
    +            scanner.addDataListener(new Scanner.DataListener() {
    +
    +              @Override
    +              public void onData(ScanDataCollection scanDataCollection) {
    +                ArrayList scanDataList = scanDataCollection.getScanData();
    +                for (ScanDataCollection.ScanData scanData : scanDataList) {
    +                  barcode = scanData.getData();
    +                  Launcher4A.instance._postEvent(Launcher4A.BARCODE_READ, 0, 0, 0, 0, 0);
    +                }
    +              }
    +            });
    +            try {
    +              scanner.read();
    +            } catch (Exception e) {
    +              e.printStackTrace();
    +            }
    +          }
    +
    +          @Override
    +          public void onClosed() {
    +
    +          }
    +        });
    +    return isActive = (results.statusCode == EMDKResults.STATUS_CODE.SUCCESS);
    +  }
    +
    +  @Override
    +  public boolean setBarcodeParam(int barcodeType, boolean enable) {
    +    return true;
    +  }
    +
    +  @Override
    +  public String getData() {
    +    String b = barcode;
    +    barcode = "";
    +    return b;
    +  }
    +
    +  @Override
    +  public boolean deactivate() {
    +    if (scanner.isEnabled()) {
    +      try {
    +        scanner.disable();
    +      } catch (Exception e) {
    +        e.printStackTrace();
    +      }
    +    }
    +    return isActive = false;
    +  }
    +
    +  @Override
    +  public boolean checkScanner(KeyEvent event) {
    +    return false;
    +  }
    +
    +  @Override
    +  public void setParam(String what, String value) {
    +  }
    +}
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/bg_circle_dark.xml b/TotalCrossVM/android/app/src/main/res/drawable/bg_circle_dark.xml
    new file mode 100644
    index 0000000000..55ea2f81c3
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/bg_circle_dark.xml
    @@ -0,0 +1,4 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/bg_circle_white.xml b/TotalCrossVM/android/app/src/main/res/drawable/bg_circle_white.xml
    new file mode 100644
    index 0000000000..6b8af80eb8
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/bg_circle_white.xml
    @@ -0,0 +1,4 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/bg_message.xml b/TotalCrossVM/android/app/src/main/res/drawable/bg_message.xml
    new file mode 100644
    index 0000000000..04c50bd24b
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/bg_message.xml
    @@ -0,0 +1,5 @@
    +
    +    
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/bg_record_button_small.xml b/TotalCrossVM/android/app/src/main/res/drawable/bg_record_button_small.xml
    new file mode 100644
    index 0000000000..ca6ea9a005
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/bg_record_button_small.xml
    @@ -0,0 +1,6 @@
    +
    +
    +    
    +    
    +    
    +
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/bg_record_button_white.xml b/TotalCrossVM/android/app/src/main/res/drawable/bg_record_button_white.xml
    new file mode 100644
    index 0000000000..4032ab0b1e
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/bg_record_button_white.xml
    @@ -0,0 +1,6 @@
    +
    +
    +    
    +    
    +    
    +
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/bg_timer.xml b/TotalCrossVM/android/app/src/main/res/drawable/bg_timer.xml
    new file mode 100644
    index 0000000000..fcd7f22ac6
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/bg_timer.xml
    @@ -0,0 +1,5 @@
    +
    +    
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_avancar.png b/TotalCrossVM/android/app/src/main/res/drawable/ic_avancar.png
    new file mode 100644
    index 0000000000..3f47d37703
    Binary files /dev/null and b/TotalCrossVM/android/app/src/main/res/drawable/ic_avancar.png differ
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_back_white.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_back_white.xml
    new file mode 100644
    index 0000000000..31fa6d49eb
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_back_white.xml
    @@ -0,0 +1,11 @@
    +
    +
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_capture.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_capture.xml
    new file mode 100644
    index 0000000000..6a914dca3f
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_capture.xml
    @@ -0,0 +1,9 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_close_white.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_close_white.xml
    new file mode 100644
    index 0000000000..6cae4ca482
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_close_white.xml
    @@ -0,0 +1,10 @@
    +
    +  
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_flash_off.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_flash_off.xml
    new file mode 100644
    index 0000000000..1045ef7e14
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_flash_off.xml
    @@ -0,0 +1,10 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_flash_on.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_flash_on.xml
    new file mode 100644
    index 0000000000..2f1f32757f
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_flash_on.xml
    @@ -0,0 +1,10 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_gallery.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_gallery.xml
    new file mode 100644
    index 0000000000..bae9494c65
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_gallery.xml
    @@ -0,0 +1,10 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_pause_new.png b/TotalCrossVM/android/app/src/main/res/drawable/ic_pause_new.png
    new file mode 100644
    index 0000000000..7ef19d7d9d
    Binary files /dev/null and b/TotalCrossVM/android/app/src/main/res/drawable/ic_pause_new.png differ
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_play_new.png b/TotalCrossVM/android/app/src/main/res/drawable/ic_play_new.png
    new file mode 100644
    index 0000000000..a28e33330c
    Binary files /dev/null and b/TotalCrossVM/android/app/src/main/res/drawable/ic_play_new.png differ
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_rec_dot.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_rec_dot.xml
    new file mode 100644
    index 0000000000..1883210cc3
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_rec_dot.xml
    @@ -0,0 +1,7 @@
    +
    +    
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_retroceder.png b/TotalCrossVM/android/app/src/main/res/drawable/ic_retroceder.png
    new file mode 100644
    index 0000000000..c3db8d9cc1
    Binary files /dev/null and b/TotalCrossVM/android/app/src/main/res/drawable/ic_retroceder.png differ
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_save_new.png b/TotalCrossVM/android/app/src/main/res/drawable/ic_save_new.png
    new file mode 100644
    index 0000000000..b0c40cdadc
    Binary files /dev/null and b/TotalCrossVM/android/app/src/main/res/drawable/ic_save_new.png differ
    diff --git a/TotalCrossVM/android/app/src/main/res/drawable/ic_switch_camera.xml b/TotalCrossVM/android/app/src/main/res/drawable/ic_switch_camera.xml
    new file mode 100644
    index 0000000000..83d362fbcc
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/drawable/ic_switch_camera.xml
    @@ -0,0 +1,10 @@
    +
    +    
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/layout/activity_image_preview.xml b/TotalCrossVM/android/app/src/main/res/layout/activity_image_preview.xml
    new file mode 100644
    index 0000000000..e2e0126035
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/layout/activity_image_preview.xml
    @@ -0,0 +1,62 @@
    +
    +
    +
    +    
    +
    +    
    +
    +    
    +    
    +
    +        
    +
    +            
    +
    +        
    +    
    +
    +
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/main/res/layout/activity_video_capture.xml b/TotalCrossVM/android/app/src/main/res/layout/activity_video_capture.xml
    new file mode 100644
    index 0000000000..f8037ce646
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/layout/activity_video_capture.xml
    @@ -0,0 +1,125 @@
    +
    +
    +
    +    
    +    
    +
    +    
    +    
    +
    +    
    +
    +    
    +    
    +
    +        
    +        
    +
    +            
    +            
    +
    +            
    +            
    +
    +            
    +            
    +
    +                
    +            
    +
    +            
    +            
    +
    +            
    +            
    +
    +        
    +
    +        
    +        
    +
    +    
    +
    +
    diff --git a/TotalCrossVM/android/app/src/main/res/layout/activity_video_player.xml b/TotalCrossVM/android/app/src/main/res/layout/activity_video_player.xml
    new file mode 100644
    index 0000000000..48cf7702a4
    --- /dev/null
    +++ b/TotalCrossVM/android/app/src/main/res/layout/activity_video_player.xml
    @@ -0,0 +1,105 @@
    +
    +
    +
    +    
    +
    +    
    +
    +    
    +
    +        
    +
    +        
    +
    +            
    +
    +            
    +
    +            
    +
    +            
    +        
    +    
    +
    +
    \ No newline at end of file
    diff --git a/TotalCrossVM/android/app/src/main/res/layout/main.xml b/TotalCrossVM/android/app/src/main/res/layout/main.xml
    index 93bdaccf3a..00afe36593 100644
    --- a/TotalCrossVM/android/app/src/main/res/layout/main.xml
    +++ b/TotalCrossVM/android/app/src/main/res/layout/main.xml
    @@ -1,34 +1,42 @@
     
     
    -  
    -  
    -  
    +
    +    
    +
    +        
    +    
    +
    +    
    +        android:orientation="horizontal">
     
    -