Skip to content

Commit d50f75c

Browse files
committed
[GR-65490] Add sqlite3 compile time options similar to CPython.
PullRequest: graalpython/3825
2 parents adea720 + b863d5c commit d50f75c

File tree

12 files changed

+519
-418
lines changed

12 files changed

+519
-418
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@ language runtime. The main focus is on user-observable behavior of the engine.
88
* `GRAALPY_VERSION_NUM` C macro now inlcudes the release level and serial number at the end to conform to the `hexversion` format. This shouldn't break any existing comparisons.
99
* `dir(foreign_object)` now returns both foreign methods and Python methods (it used to return only foreign methods).
1010
* Support `__name__`, `__doc__`, `__text_signature__` fields on foreign executables to serve as their proper counterparts on the Python side. This is useful to, for example, use Java functional interfaces in lieu of Python functions for things like LangChain's `@tool` annotation that want to inspect the underlying function.
11-
* Remove support for running C extensions as LLVM bitcode. This also removes the related options `python.UseSystemToolchain` and `python.NativeModules`.
11+
* Remove support for running C extensions as LLVM bitcode. This also removes the related options `python.UseSystemToolchain` and `python.NativeModules`. Isolation of native code when embedding GraalPy into Java projects is now provided via the GraalVM enterprise edition polyglot isolate feature, which can launch in a separate external sub-process by setting the `--engine.IsolateMode=external` option.
1212
* Remove built-in HPy module. HPy can now be installed and used from the upstream sources.
13+
* Update Python standard library and core to 3.12.8.
14+
* Implement `faulthandler.dump_traceback_later` to better support testing frameworks that implement resilience to crashes.
15+
* Fix various issues affecting cibuildwheels on Windows, to make it easier for Python projects to provide native extensions for GraalPy on all supported platforms.
16+
* Add support for sharing Arrow arrays and tables between Java, PyArrow, and Pandas to avoid data copying when embedding those libraries into a Java project.
17+
* Enable FTS3, FTS4, FTS5, RTREE, and math function features in the bundled sqlite3 library.
18+
* Add support patches for Torch 2.7.0, PyGObject 3.52.3, xmlschema 4.0.0, lxml < 5.4.0, SciPy 1.15, jq 1.8.0, NumPy < 2.3, ormsgpack < 1.9.1, pandas 2.2.3, PyArrow 19.0, PyMuPDF 1.25.4.
1319

1420
## Version 24.2.0
1521
* Updated developer metadata of Maven artifacts.

graalpython/com.oracle.graal.python.cext/CMakeLists.txt

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ if (MSVC)
8080
/wd4710 # 'fprintf': function not inlined
8181
/wd4706 # assignment within conditional expression
8282
/wd4774 # 'sprintf': format string expected in argument 2 is not a string literal
83+
/wd4191 # unsafe conversion from 'PyObject *(__cdecl *)(MatchObject *,PyObject *const *,Py_ssize_t)' to 'void (__cdecl *)(void)'
84+
/wd4574 # sqlite, expat: 'SQLITE_ATOMIC_INTRINSICS' is defined to be '0': did you mean to use '#if SQLITE_ATOMIC_INTRINSICS'?
85+
/wd4701 # potentially uninitialized local variable used
8386

8487
# Some that I'm not so happy about
8588
/wd4232 # sre: nonstandard extension used: 'ml_meth': address of dllimport 'Py_GenericAlias' is not static, identity not guaranteed
86-
/wd4191 # sre and sqlite: 'type cast': like unsafe conversion from 'PyObject *(__cdecl *)(MatchObject *,PyObject *const *,Py_ssize_t)' to 'void (__cdecl *)(void)'
8789
/wd4918 # sre: invalid character in pragma optimization list
88-
/wd4701 # cpython_unicodedata: potentially uninitialized local variable 'rc' used
8990
/wd4703 # unicodeobject.c:potentially uninitialized local pointer variable used
90-
/wd4574 # sqlite: 'SQLITE_ATOMIC_INTRINSICS' is defined to be '0': did you mean to use '#if SQLITE_ATOMIC_INTRINSICS'?
9191
/wd4310 # xmlparse: cast truncates constant value
9292
/wd4777 # format string '%zd' requires an argument of type 'unsigned __int64'
9393
)
@@ -238,7 +238,19 @@ set(SQLITE3_SRC
238238
"${SRC_DIR}/modules/_sqlite/util.c"
239239
)
240240
native_module("_sqlite3" TRUE "${SQLITE3_SRC}")
241+
# This combines the flags CPython uses on macOS and Windows, on Linux systems
242+
# it usually links against the system sqlite3 so we do not really know. See
243+
# https://github.com/python/cpython/issues/88017 for some reasons
241244
target_include_directories("_sqlite3" PUBLIC "${SRC_DIR}/modules/_sqlite/sqlite")
245+
target_compile_definitions("_sqlite3" PRIVATE
246+
SQLITE_ENABLE_MATH_FUNCTIONS
247+
SQLITE_ENABLE_FTS5
248+
SQLITE_ENABLE_FTS4
249+
SQLITE_ENABLE_FTS3_PARENTHESIS
250+
SQLITE_ENABLE_RTREE
251+
SQLITE_OMIT_AUTOINIT
252+
SQLITE_TCL=0
253+
)
242254

243255
set(LIBHACL_HEADERS
244256
# "${SRC_DIR}/modules/_hacl/include/krml/FStar_UInt128_Verified.h"
@@ -299,13 +311,19 @@ set(TESTCAPI_SRC
299311
"${SRC_DIR}/modules/_testcapi.c"
300312
)
301313

302-
if(NOT WIN32)
303-
native_module("_testcapi" FALSE "${TESTCAPI_SRC}")
304-
simple_native_module("_testbuffer")
305-
simple_native_module("_testmultiphase")
306-
simple_native_module("_testsinglephase")
307-
simple_native_module("_ctypes_test")
314+
native_module("_testcapi" FALSE "${TESTCAPI_SRC}")
315+
if(WIN32)
316+
target_compile_options("_testcapi" PRIVATE /wd4296 /wd4130)
317+
endif()
318+
simple_native_module("_testbuffer")
319+
if(WIN32)
320+
target_compile_options("_testbuffer" PRIVATE /wd4090)
321+
endif()
322+
simple_native_module("_testmultiphase")
323+
simple_native_module("_testsinglephase")
324+
simple_native_module("_ctypes_test")
308325

326+
if(NOT WIN32)
309327
###################### BZIP2 ########################
310328
if(DEFINED LIBBZ2_BUILD_FILE)
311329
include("${LIBBZ2_BUILD_FILE}")

graalpython/com.oracle.graal.python.frozen/freeze_modules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ def write_frozen_module_file(file, modules):
607607
stat_result = os.stat(file)
608608
atime, mtime = stat_result.st_atime, stat_result.st_mtime
609609
else:
610+
linesep = os.linesep
610611
content = None
611612
os.makedirs(os.path.dirname(file), exist_ok=True)
612613
with open(file, "w", encoding="utf-8", newline=linesep) as out_file:

graalpython/com.oracle.graal.python.pegparser.generator/CMakeLists.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ add_custom_target(grammar ALL
3737
DEPENDS "${PARSER_OUTPUT}")
3838
add_custom_command(
3939
OUTPUT "${PARSER_OUTPUT}"
40-
COMMAND ${PYTHON_EXE} "${CMAKE_CURRENT_LIST_DIR}/main_parser_gen.py" "${GRAMMAR}" "${TOKENS}" "${PARSER_OUTPUT}"
41-
COMMAND ${CMAKE_COMMAND} -E copy_if_different
42-
${PARSER_OUTPUT}
43-
${PARSER_TARGET}
40+
COMMAND ${PYTHON_EXE} "${CMAKE_CURRENT_LIST_DIR}/main_parser_gen.py" "${GRAMMAR}" "${TOKENS}" "${PARSER_TARGET}"
4441
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/main_parser_gen.py" "${GRAMMAR}" "${TOKENS}" "${PARSER_TARGET}" ${PEGEN_FILES} ${PEGJAVA_FILES})
4542

4643
add_custom_target(asdl ALL

graalpython/com.oracle.graal.python.pegparser.generator/asdl/java_file.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,24 @@ def define(self, line: str, *annotations: str):
131131
def create(out_dir_base: str, java_package: str, java_class_name: str):
132132
sst_dir = os.path.join(out_dir_base, *java_package.split('.'))
133133
os.makedirs(sst_dir, exist_ok=True)
134-
with open(os.path.join(sst_dir, java_class_name + '.java'), 'w') as f:
134+
filename = os.path.join(sst_dir, java_class_name + '.java')
135+
136+
# Determine which line separator to use
137+
if os.path.exists(filename):
138+
with open(filename, "r", encoding="utf-8", newline=os.linesep) as f:
139+
content = f.read()
140+
if os.linesep != "\n":
141+
if content.replace(os.linesep, "\n") == content:
142+
# Windows file has Unix line endings
143+
linesep = "\n"
144+
else:
145+
linesep = os.linesep
146+
else:
147+
linesep = "\n"
148+
else:
149+
linesep = os.linesep
150+
151+
with open(filename, 'w', encoding="utf-8", newline=linesep) as f:
135152
emitter = Emitter(f)
136153
f.write(HEADER)
137154
f.write(f'package {java_package};\n')

graalpython/com.oracle.graal.python.pegparser.generator/main_parser_gen.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python3
2-
# Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
33
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
#
55
# The Universal Permissive License (UPL), Version 1.0
@@ -69,10 +69,37 @@ def main():
6969
with open(args.tokens_file, "r") as tok_file:
7070
all_tokens, exact_tokens, non_exact_tokens = generate_token_definitions(tok_file)
7171

72-
with open(args.output_file, "w") as file:
72+
# Determine which line separator to use
73+
if os.path.exists(args.output_file):
74+
stat_result = os.stat(args.output_file)
75+
atime, mtime = stat_result.st_atime, stat_result.st_mtime
76+
with open(args.output_file, "r", encoding="utf-8", newline=os.linesep) as f:
77+
content = f.read()
78+
if os.linesep != "\n":
79+
unix_content = content.replace(os.linesep, "\n")
80+
if unix_content == content:
81+
# Windows file has Unix line endings
82+
linesep = "\n"
83+
content = unix_content
84+
else:
85+
linesep = os.linesep
86+
else:
87+
linesep = "\n"
88+
else:
89+
content = None
90+
linesep = os.linesep
91+
92+
with open(args.output_file, "w", encoding="utf-8", newline=linesep) as file:
7393
gen = JavaParserGenerator(grammar, all_tokens, exact_tokens, non_exact_tokens, file, debug=args.debug)
7494
gen.generate(os.path.basename(args.grammar_file))
7595

96+
with open(args.output_file, "r", encoding="utf-8", newline=linesep) as file:
97+
new_content = file.read()
98+
99+
if content == new_content:
100+
print(f"{args.output_file} not modified")
101+
os.utime(args.output_file, (atime, mtime))
102+
76103

77104
if __name__ == '__main__':
78105
main()

graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,10 @@ private static Path resolvePath(Path path) {
748748
"_PyCFunction_SetModule", "_PyCFunction_SetMethodDef",
749749
"PyCode_GetFileName", "_PyArray_Resize", "_PyArray_Data",
750750
"_PyErr_Occurred", "_PyNamespace_New", "_Py_GetErrorHandler",
751+
// Not actually additional, only defined on Windows.
752+
// TODO: fix generated CAPIFunctions.txt
753+
"PyUnicode_AsMBCSString", "PyUnicode_EncodeCodePage", "PyUnicode_DecodeMBCS",
754+
"PyUnicode_DecodeCodePageStateful", "PyUnicode_DecodeMBCSStateful",
751755
};
752756

753757
public String resolveArgDescriptor(String sig) {

graalpython/com.oracle.graal.python.test/src/tests/test_sqlite3.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -49,3 +49,21 @@ def test_basic_functionality():
4949
rows = conn.execute("select sqlite_version()")
5050
assert len(next(rows)[0]) >= 5
5151
conn.close()
52+
53+
54+
def test_fts5_works():
55+
# we explicitly enable those features below, but on CPython they might not
56+
# be available if using some system libsqlite that doesn't have them
57+
import sqlite3
58+
conn = sqlite3.connect(':memory:')
59+
try:
60+
conn.execute("CREATE VIRTUAL TABLE IF NOT EXISTS your_table USING fts5(column1, column2)")
61+
conn.execute("CREATE VIRTUAL TABLE IF NOT EXISTS your_table USING fts4(column1, column2)")
62+
conn.execute("CREATE VIRTUAL TABLE IF NOT EXISTS your_table USING fts3(column1, column2)")
63+
conn.execute("CREATE VIRTUAL TABLE IF NOT EXISTS your_table USING rtree(column1, column2)")
64+
sqpi = next(conn.execute("SELECT pi()"))[0]
65+
assert 3.14 == float(f'{sqpi:.2f}'), sqpi
66+
except sqlite3.OperationalError:
67+
import sys
68+
assert sys.implementation.name != "graalpy"
69+
conn.close()

0 commit comments

Comments
 (0)