# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/dcheck_always_on.gni")
import("//build/config/sanitizers/sanitizers.gni")
import("//testing/libfuzzer/fuzzer_test.gni")

# Compile-time options passed to SQLite.
#
# These options are used when building our own SQLite library, which happens
# everywhere except on iOS. These compile-time options are exported via a
# public_config to all targets using SQLite, because they're needed by the
# sqlite.h header. To avoid name clashes (macro names are resolved using a
# global namespace), this block should only contain preprocessor macros that
# are unambiguously connected to SQLite.
#
# The vast majority of the macros here are documented at
# https://www.sqlite.org/compile.html
config("chromium_sqlite3_compile_options") {
  defines = [
    "SQLITE_ENABLE_FTS3",

    # New unicode61 tokenizer with built-in tables.
    "SQLITE_DISABLE_FTS3_UNICODE",

    # Chrome does not enable fts4, disable extra code.
    "SQLITE_DISABLE_FTS4_DEFERRED",
    "SQLITE_ENABLE_ICU",

    # Defaults the secure_delete pragma to 1.
    #
    # This causes SQLite to overwrite all deleted information with zeroes,
    # trading additional I/O for better privacy guarantees.
    "SQLITE_SECURE_DELETE",

    # TODO(pwnall): SQLite adds mutexes to protect structures which cross
    # threads. In theory Chrome should be able to turn this to "2" which
    # should give a slight speed boost. "2" is safe as long as a single
    # connection is not used by more than one thread at a time.
    "SQLITE_THREADSAFE=1",

    # SQLite can spawn threads to sort in parallel if configured
    # appropriately.  Chrome doesn't configure SQLite for that, and would
    # prefer to control distribution to worker threads.
    "SQLITE_MAX_WORKER_THREADS=0",

    # Allow 256MB mmap footprint per connection.  Should not be too open-ended
    # as that could cause memory fragmentation.  50MB encompasses the 99th
    # percentile of Chrome databases in the wild.
    # TODO(pwnall): A 64-bit-specific value could be 1G or more.
    # TODO(pwnall): Figure out if exceeding this is costly.
    "SQLITE_MAX_MMAP_SIZE=268435456",

    # The default POSIX permissions for a newly created SQLite database.
    #
    # If unspecified, this defaults to 0644. All the data stored by Chrome is
    # private, so our databases use stricter settings.
    "SQLITE_DEFAULT_FILE_PERMISSIONS=0600",

    # SQLite uses a lookaside buffer to improve performance of small mallocs.
    # Chrome already depends on small mallocs being efficient, so we disable
    # this to avoid the extra memory overhead.
    "SQLITE_DEFAULT_LOOKASIDE=0,0",

    # Needed by the SQL MemoryDumpProvider.
    #
    # Setting this to 1 is needed to collect the information reported by
    # sqlite3_status64(SQLITE_STATUS_MEMORY_USED). Without this setting, the API
    # still exists, but does not work as promised.
    "SQLITE_DEFAULT_MEMSTATUS=1",

    # Must match sql::Database::kDefaultPageSize.
    "SQLITE_DEFAULT_PAGE_SIZE=4096",

    # By default SQLite pre-allocates 100 pages of pcache data, which will not
    # be released until the handle is closed.  This is contrary to Chrome's
    # memory-usage goals.
    "SQLITE_DEFAULT_PCACHE_INITSZ=0",

    # Some defines can affect the amalgamation. Those should be added to
    # google_generate_amalgamation.sh, and the amalgamation re-generated.
    # Usually this involves disabling features which include keywords or
    # syntax, for instance SQLITE_OMIT_VIRTUALTABLE omits the virtual table
    # syntax entirely. Missing an item usually results in syntax working but
    # execution failing. Review:
    #   src/src/parse.py
    #   src/tool/mkkeywordhash.c

    # The flags below are recommended in the SQLite documentation, and disable
    # features Chrome doesn't use.
    "SQLITE_LIKE_DOESNT_MATCH_BLOBS",
    "SQLITE_OMIT_DEPRECATED",
    "SQLITE_OMIT_PROGRESS_CALLBACK",
    "SQLITE_OMIT_SHARED_CACHE",
    "SQLITE_USE_ALLOCA",

    # Chrome initializes SQLite manually in //sql/connection.cc.
    "SQLITE_OMIT_AUTOINIT",

    # Chrome calls sqlite3_reset() correctly to reset prepared statements.
    "SQLITE_OMIT_AUTORESET",

    # Chromium does not use sqlite3_{get,free}_table().
    # Chrome doesn't use sqlite3_compileoption_{used,get}().
    "SQLITE_OMIT_COMPILEOPTION_DIAGS",

    # Chrome doesn't ship the SQLite shell, so command auto-completion is not
    # needed. Chrome developers who build the SQLite shell living at
    # //third_party/sqlite:sqlite_shell for diagnostic purposes will have to
    # live without auto-completion.
    "SQLITE_OMIT_COMPLETE",

    # Chrome does not use sqlite3_{get,free}_table().
    "SQLITE_OMIT_GET_TABLE",

    # Chrome does not use sqlite3_{enable_}load_extension().
    # Asides from giving us fairly minor code savings, this option disables code
    # that breaks our method for renaming SQLite's exported symbols. Last,
    # there's a tiny security benefit to knowing that WebSQL can't possibly
    # reach extension loading code.
    "SQLITE_OMIT_LOAD_EXTENSION",

    # Chrome doesn't use TCL variables.
    "SQLITE_OMIT_TCL_VARIABLE",

    # Chrome doesn't use sqlite3_{profile,trace}().
    "SQLITE_OMIT_TRACE",

    # Chrome doesn't use UPSERT.
    "SQLITE_OMIT_UPSERT",

    # Chrome doesn't use window functions in SQL.
    "SQLITE_OMIT_WINDOWFUNC",

    # Uses isnan() in the C99 standard library.
    "SQLITE_HAVE_ISNAN",
  ]

  # On OSX, SQLite has extra logic for detecting the use of network
  # filesystems (e.g., AFS, NFS) and for working around locking problems in
  # these filesystems. This logic is gated by SQLITE_ENABLE_LOCKING_STYLE, which
  # is 1 by default on OSX and iOS, and 0 everywhere else.
  #
  # When enabled, SQLITE_ENABLE_LOCKING_STYLE results in a compile-time warning
  # on iOS. The recommended solution is to disable the flag on iOS, because
  # iOS doesn't (yet?) have networked filesystems. Since we have to do this,
  # might as well be explicit about the flag everywhere.
  if (is_mac) {
    defines += [ "SQLITE_ENABLE_LOCKING_STYLE=1" ]
  } else {
    defines += [ "SQLITE_ENABLE_LOCKING_STYLE=0" ]
  }

  if (using_sanitizer) {
    # Limit max length of data blobs and queries to 128 MB for fuzzing builds.
    defines += [
      "SQLITE_MAX_LENGTH=128000000",
      "SQLITE_MAX_SQL_LENGTH=128000000",
      "SQLITE_PRINTF_PRECISION_LIMIT=1280000",
    ]

    # During fuzz testing, valid SQL queries generated by fuzzing engine may
    # lead to large memory allocations. If that happens, fuzzer reports an
    # out-of-memory error. However, such errors are not valid bugs.
    # To avoid hitting those irrelevant OOMs, we limit max number of memory
    # pages, so fuzzer will not crash when reaching the limit.
    # Apply this for fuzzing builds only, not for all builds with sanitizers.
    if (use_fuzzing_engine) {
      defines += [ "SQLITE_MAX_PAGE_COUNT=16384" ]
    }
  }

  if (is_debug || dcheck_always_on) {
    if (use_fuzzing_engine && use_sanitizer_coverage) {
      # Enable SQLite's assert() macros.
      #
      # TODO(pwnall): Fix all the bugs preventing us from enabling this flag for
      #               all DCHECK builds. See https://crbug.com/907371 and
      #               https://crrev.com/c/1343529.
      defines += [ "SQLITE_DEBUG" ]
    }

    # Check preconditions when SQLite APIs are called. See
    # https://sqlite.org/compile.html#enable_api_armor
    #
    # fuzzing builds have this disabled because the fuzzers are guaranteed to
    # use the API correctly, and removing the checks opens up the possibility
    # that the fuzzers will get more code coverage.
    defines += [ "SQLITE_ENABLE_API_ARMOR" ]
  }
}

config("sqlite_warnings") {
  cflags = []
  if (is_clang) {
    # sqlite contains a few functions that are unused, at least on
    # Windows with Chrome's sqlite patches applied
    # (interiorCursorEOF fts3EvalDeferredPhrase
    # fts3EvalSelectDeferred sqlite3Fts3InitHashTable
    # sqlite3Fts3InitTok).
    cflags += [ "-Wno-unused-function" ]

    if (is_debug || dcheck_always_on) {
      cflags += [
        # SQLite uses assert(!"description") to express
        # NOT_REACHED() << "description". This is considered an implicit
        # conversion from char[] to bool, and triggers a warning.
        "-Wno-string-conversion",
      ]
    }
  }
  if (is_linux) {
    cflags += [
      # SQLite doesn't believe in compiler warnings, preferring testing.
      # http://www.sqlite.org/faq.html#q17
      "-Wno-int-to-pointer-cast",
      "-Wno-pointer-to-int-cast",
    ]
  }
  if (is_ios) {
    cflags += [
      # SQLite issues a #pragma warning on iOS.
      # http://sqlite.1065341.n5.nabble.com/Compiler-warning-quot-gethostuuid-is-disabled-quot-building-SQLite-for-iOS-td96881.html
      #
      # Contrary to what is said on the mailing list, setting
      # SQLITE_ENABLE_LOCKING_STYLE to 0 does not make the warning go away.
      "-Wno-#warnings",
    ]
  }
  if (is_win && !is_clang) {
    cflags += [ "/wd4101" ]  # 'zTrace' unreferenced variable in src/vdbe.c
  }
}

# Naming the library "sqlite3" can cause conflicts with the system library.
component("chromium_sqlite3") {
  visibility = [ ":*" ]

  public = [
    "sqlite3.h",
    "src/src/recover.h",
  ]

  sources = [
    "amalgamation/sqlite3.h",
    "sqlite3_shim.c",
    "src/src/recover.c",
    "src/src/recover_varint.c",
  ]

  inputs = [
    # This file is #included into sqlite3_shim.c, which injects Chrome-specific
    # definitions into the SQLite amalgamation code.
    "amalgamation/sqlite3.c",
  ]

  cflags = []
  defines = []

  if (is_component_build) {
    if (is_win) {
      defines += [ "SQLITE_API=__declspec(dllexport)" ]
    } else {
      defines += [ "SQLITE_API=__attribute__((visibility(\"default\")))" ]
    }
  }

  if (is_linux || is_android) {
    defines += [
      # Linux provides fdatasync(), a faster equivalent of fsync().
      "fdatasync=fdatasync",
    ]
  }

  if (is_posix || is_fuchsia) {
    defines += [
      # Allow xSleep() call on Unix to use usleep() rather than sleep(), so it
      # will have microsecond precision.  Should only affect contended
      # databases via the busy callback.  Browser profile databases are mostly
      # exclusive, but renderer databases may allow for contention.
      "HAVE_USLEEP=1",

      # Use pread/pwrite directly rather than emulating them.
      "USE_PREAD=1",
    ]
  }

  include_dirs = [
    ".",  # sqlite3.h here must override the one in amalgamation/.
    "amalgamation",
  ]

  configs -= [ "//build/config/compiler:chromium_code" ]
  configs += [
    ":chromium_sqlite3_compile_options",
    "//build/config/compiler:no_chromium_code",
    "//build/config/sanitizers:cfi_icall_generalize_pointers",

    # Must be after no_chromium_code for warning flags to be ordered correctly.
    ":sqlite_warnings",
  ]

  if (is_mac || is_ios) {
    libs = [ "CoreFoundation.framework" ]
    if (!is_ios) {
      libs += [ "CoreServices.framework" ]
    }
  } else if (is_android) {
    defines += [
      "SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576",
      "SQLITE_DEFAULT_AUTOVACUUM=1",
      "SQLITE_TEMP_STORE=3",
    ]
  }

  deps = [
    "//third_party/icu",
  ]
}

config("sqlite_export") {
  if (is_component_build && is_win) {
    defines = [ "SQLITE_API=__declspec(dllimport)" ]
  }
}

# This is used to allow the SQLITE_API definition to be different when
# building sqlite3.c than it is when clients include sqlite3.h.
group("sqlite") {
  public_deps = [
    ":chromium_sqlite3",
  ]
  public_configs = [
    ":chromium_sqlite3_compile_options",
    ":sqlite_export",
  ]
}

if (is_linux || is_mac) {
  executable("sqlite_shell") {
    include_dirs = [
      # shell.c contains an '#include "sqlite3.h", which we want to be
      # resolved to //third_party/sqlite/shell.h.
      ".",
    ]

    sources = [
      "amalgamation/shell/shell.c",

      # Include a dummy c++ file to force linking of libstdc++.
      "build_as_cpp.cc",
    ]

    if (is_linux) {
      sources += [ "src/src/shell_icu_linux.c" ]
    }

    # TODO(pwnall): Re-enable the shell on Windows when compilation errors are
    #               fixed upstream.
    if (is_win) {
      sources += [ "src/src/shell_icu_win.c" ]
    }

    deps = [
      ":sqlite",
      "//third_party/icu",
    ]

    configs -= [ "//build/config/compiler:chromium_code" ]
    configs += [
      ":chromium_sqlite3_compile_options",
      "//build/config/compiler:no_chromium_code",

      # Must be after no_chromium_code for warning flags to be ordered
      # correctly.
      ":sqlite_warnings",
    ]
  }
}

fuzzer_test("sqlite3_ossfuzz_fuzzer") {
  include_dirs = [ "." ]
  sources = [
    "src/test/ossfuzz.c",
  ]
  deps = [
    ":sqlite",
  ]
  dict = "fuzz/sql.dict"
}
