""" Android Database 组件 — Cursor, MatrixCursor, ContentObserver 等 方法签名完全对齐 Android SDK """ import os import sys from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union from .._core import ( Stub, AndroidOnlyError, android_context, resolve_android_path, to_android_path, _sdcard_root, _app_data_dir, _package_name, ) # ═══════════════════════════════════════════════════════════════ # SQLException hierarchy # ═══════════════════════════════════════════════════════════════ class SQLException(RuntimeError): """SQLException — 数据库异常基类""" def __init__(self, message: str = ""): super().__init__(message) self._error_message = message def getMessage(self) -> str: return self._error_message def toString(self) -> str: return f"SQLException: {self._error_message}" class CursorIndexOutOfBoundsException(SQLException): """CursorIndexOutOfBoundsException — 游标越界""" def __init__(self, message: str = ""): super().__init__(message) # ═══════════════════════════════════════════════════════════════ # ContentObserver # ═══════════════════════════════════════════════════════════════ class ContentObserver(Stub): """ContentObserver — 内容观察者""" def __init__(self, handler: Any = None): super().__init__() self._handler = handler self._notify_for_descendants = False def onChange(self, self_change: bool = True): pass def onChange(self, self_change: bool, uri: Any = None): # type: ignore pass def deliverSelfNotifications(self) -> bool: return True def getSelfChangeNotify(self) -> bool: return True def dispatchChange(self, self_change: bool, uri: Any = None): self.onChange(self_change, uri) # ═══════════════════════════════════════════════════════════════ # DataSetObserver # ═══════════════════════════════════════════════════════════════ class DataSetObserver(Stub): """DataSetObserver — 数据集观察者""" def onChanged(self): pass def onInvalidated(self): pass # ═══════════════════════════════════════════════════════════════ # ContentObservable # ═══════════════════════════════════════════════════════════════ class ContentObservable(Stub): """ContentObservable — 内容可观察对象""" def __init__(self): super().__init__() self._observers: List[ContentObserver] = [] def registerObserver(self, observer: ContentObserver): if observer not in self._observers: self._observers.append(observer) def unregisterObserver(self, observer: ContentObserver): self._observers = [o for o in self._observers if o is not observer] def dispatchChange(self, self_change: bool, uri: Any = None): for observer in list(self._observers): observer.dispatchChange(self_change, uri) def notifyChange(self, self_change: bool): for observer in list(self._observers): observer.onChange(self_change) # ═══════════════════════════════════════════════════════════════ # DataSetObservable # ═══════════════════════════════════════════════════════════════ class DataSetObservable(Stub): """DataSetObservable — 数据集可观察对象""" def __init__(self): super().__init__() self._observers: List[DataSetObserver] = [] def registerObserver(self, observer: DataSetObserver): if observer not in self._observers: self._observers.append(observer) def unregisterObserver(self, observer: DataSetObserver): self._observers = [o for o in self._observers if o is not observer] def notifyChanged(self): for observer in list(self._observers): observer.onChanged() def notifyInvalidated(self): for observer in list(self._observers): observer.onInvalidated() # ═══════════════════════════════════════════════════════════════ # CharArrayBuffer # ═══════════════════════════════════════════════════════════════ class CharArrayBuffer(Stub): """CharArrayBuffer — 字符数组缓冲区""" def __init__(self, size: int = 0): super().__init__() self.data: List[str] = [""] * size # ═══════════════════════════════════════════════════════════════ # DatabaseErrorHandler # ═══════════════════════════════════════════════════════════════ class DatabaseErrorHandler(Stub): """DatabaseErrorHandler — 数据库错误处理器""" def onCorruption(self, db_obj: Any): pass class DefaultDatabaseErrorHandler(DatabaseErrorHandler): """DefaultDatabaseErrorHandler — 默认数据库错误处理器""" def onCorruption(self, db_obj: Any): import sys sys.stderr.write(f"[DatabaseError] Corruption detected for {db_obj}\n") # ═══════════════════════════════════════════════════════════════ # CursorWindow # ═══════════════════════════════════════════════════════════════ class CursorWindow(Stub): """CursorWindow — 游标窗口""" def __init__(self, name: str = "unnamed"): super().__init__() self._name = name self._start_position: int = 0 self._num_rows: int = 0 self._num_columns: int = 0 self._data: List[List[Optional[Any]]] = [] def setStartPosition(self, pos: int): self._start_position = pos def getStartPosition(self) -> int: return self._start_position def allocRow(self) -> bool: self._data.append([None] * self._num_columns) self._num_rows = len(self._data) return True def freeRow(self, row: int): if 0 <= row < len(self._data): self._data[row] = [None] * self._num_columns def freeLastRow(self): if self._data: self._data.pop() self._num_rows = len(self._data) def getNumRows(self) -> int: return self._num_rows def setNumColumns(self, num_columns: int): self._num_columns = num_columns def getNumColumns(self) -> int: return self._num_columns def writeString(self, value: str, row: int, col: int): while len(self._data) <= row: self._data.append([None] * self._num_columns) while len(self._data[row]) <= col: self._data[row].append(None) self._data[row][col] = value def writeLong(self, value: int, row: int, col: int): while len(self._data) <= row: self._data.append([None] * self._num_columns) while len(self._data[row]) <= col: self._data[row].append(None) self._data[row][col] = value def writeDouble(self, value: float, row: int, col: int): while len(self._data) <= row: self._data.append([None] * self._num_columns) while len(self._data[row]) <= col: self._data[row].append(None) self._data[row][col] = value def writeBlob(self, value: bytes, row: int, col: int): while len(self._data) <= row: self._data.append([None] * self._num_columns) while len(self._data[row]) <= col: self._data[row].append(None) self._data[row][col] = value def writeNull(self, row: int, col: int): while len(self._data) <= row: self._data.append([None] * self._num_columns) while len(self._data[row]) <= col: self._data[row].append(None) self._data[row][col] = None def getString(self, row: int, col: int) -> Optional[str]: try: v = self._data[row][col] return str(v) if v is not None else None except (IndexError, TypeError): return None def getLong(self, row: int, col: int) -> int: try: v = self._data[row][col] return int(v) if v is not None else 0 except (IndexError, ValueError, TypeError): return 0 def getDouble(self, row: int, col: int) -> float: try: v = self._data[row][col] return float(v) if v is not None else 0.0 except (IndexError, ValueError, TypeError): return 0.0 def getBlob(self, row: int, col: int) -> Optional[bytes]: try: v = self._data[row][col] if isinstance(v, bytes): return v if isinstance(v, str): return v.encode("utf-8") return None except IndexError: return None def isNull(self, row: int, col: int) -> bool: try: return self._data[row][col] is None except IndexError: return True def clear(self): self._data.clear() self._num_rows = 0 def toString(self) -> str: return f"CursorWindow(name={self._name}, rows={self._num_rows}, cols={self._num_columns})" # ═══════════════════════════════════════════════════════════════ # Cursor interface # ═══════════════════════════════════════════════════════════════ class Cursor(Stub): """Cursor — 游标接口""" FIELD_TYPE_NULL = 0 FIELD_TYPE_INTEGER = 1 FIELD_TYPE_FLOAT = 2 FIELD_TYPE_STRING = 3 FIELD_TYPE_BLOB = 4 def __init__(self): super().__init__() self._pos: int = -1 self._closed: bool = False def getCount(self) -> int: return 0 def getPosition(self) -> int: return self._pos def moveToFirst(self) -> bool: return self.moveToPosition(0) def moveToLast(self) -> bool: return self.moveToPosition(self.getCount() - 1) def moveToNext(self) -> bool: return self.moveToPosition(self._pos + 1) def moveToPrevious(self) -> bool: return self.moveToPosition(self._pos - 1) def move(self, offset: int) -> bool: return self.moveToPosition(self._pos + offset) def moveToPosition(self, position: int) -> bool: count = self.getCount() if position >= count: self._pos = count return False if position < -1: self._pos = -1 return False self._pos = position return True def isFirst(self) -> bool: return self._pos == 0 and self.getCount() > 0 def isLast(self) -> bool: count = self.getCount() return count > 0 and self._pos == count - 1 def isBeforeFirst(self) -> bool: if self.getCount() == 0: return True return self._pos < 0 def isAfterLast(self) -> bool: if self.getCount() == 0: return True return self._pos >= self.getCount() def getColumnIndex(self, column_name: str) -> int: names = self.getColumnNames() try: return names.index(column_name) except ValueError: return -1 def getColumnIndexOrThrow(self, column_name: str) -> int: idx = self.getColumnIndex(column_name) if idx < 0: raise IllegalArgumentException(f"Column '{column_name}' does not exist") return idx def getColumnName(self, column_index: int) -> str: names = self.getColumnNames() if 0 <= column_index < len(names): return names[column_index] return "" def getColumnNames(self) -> List[str]: return [] def getColumnCount(self) -> int: return len(self.getColumnNames()) def getType(self, column_index: int) -> int: return self.FIELD_TYPE_NULL def getString(self, column_index: int) -> Optional[str]: return None def getInt(self, column_index: int) -> int: return 0 def getLong(self, column_index: int) -> int: return 0 def getFloat(self, column_index: int) -> float: return 0.0 def getDouble(self, column_index: int) -> float: return 0.0 def getShort(self, column_index: int) -> int: return 0 def getBlob(self, column_index: int) -> Optional[bytes]: return None def getBytes(self, column_index: int) -> Optional[bytes]: return self.getBlob(column_index) def close(self): self._closed = True def isClosed(self) -> bool: return self._closed def requery(self) -> bool: return False def deactivate(self): pass def registerContentObserver(self, observer: ContentObserver): pass def unregisterContentObserver(self, observer: ContentObserver): pass def registerDataSetObserver(self, observer: DataSetObserver): pass def unregisterDataSetObserver(self, observer: DataSetObserver): pass def getWantsAllOnMoveCalls(self) -> bool: return False def getExtras(self) -> Any: return None def respond(self, extras: Any) -> Any: return None def setNotificationUri(self, cr: Any, uri: Any): pass def getNotificationUri(self) -> Any: return None def fillWindow(self, pos: int, window: CursorWindow): pass def copyStringToBuffer(self, column_index: int, buffer: CharArrayBuffer): val = self.getString(column_index) if val is not None: buffer.data = list(val) def isNull(self, column_index: int) -> bool: return self.getType(column_index) == self.FIELD_TYPE_NULL # ═══════════════════════════════════════════════════════════════ # AbstractCursor # ═══════════════════════════════════════════════════════════════ class AbstractCursor(Cursor): """AbstractCursor — 游标抽象基类""" def __init__(self): super().__init__() self._content_observable = ContentObservable() self._data_set_observable = DataSetObservable() self._notification_uri: Any = None self._self_observer: Optional[ContentObserver] = None def getCount(self) -> int: return 0 def getColumnNames(self) -> List[str]: return [] def registerContentObserver(self, observer: ContentObserver): self._content_observable.registerObserver(observer) def unregisterContentObserver(self, observer: ContentObserver): self._content_observable.unregisterObserver(observer) def registerDataSetObserver(self, observer: DataSetObserver): self._data_set_observable.registerObserver(observer) def unregisterDataSetObserver(self, observer: DataSetObserver): self._data_set_observable.unregisterObserver(observer) def setNotificationUri(self, cr: Any, uri: Any): self._notification_uri = uri def getNotificationUri(self) -> Any: return self._notification_uri def onChange(self, self_change: bool): self._content_observable.dispatchChange(self_change) def getWantsAllOnMoveCalls(self) -> bool: return False def getExtras(self) -> Any: return None def respond(self, extras: Any) -> Any: return None def fillWindow(self, pos: int, window: CursorWindow): pass # ═══════════════════════════════════════════════════════════════ # AbstractWindowedCursor # ═══════════════════════════════════════════════════════════════ class AbstractWindowedCursor(AbstractCursor): """AbstractWindowedCursor — 带窗口的游标""" def __init__(self): super().__init__() self._window: Optional[CursorWindow] = None def getWindow(self) -> Optional[CursorWindow]: return self._window def setWindow(self, window: CursorWindow): self._window = window def hasWindow(self) -> bool: return self._window is not None def clearBlobCache(self): pass def fillWindow(self, pos: int, window: CursorWindow): if self._window: self._window.clear() def getString(self, column_index: int) -> Optional[str]: if self._window and self._pos >= 0: return self._window.getString(self._pos, column_index) return None def getInt(self, column_index: int) -> int: if self._window and self._pos >= 0: return self._window.getLong(self._pos, column_index) return 0 def getLong(self, column_index: int) -> int: if self._window and self._pos >= 0: return self._window.getLong(self._pos, column_index) return 0 def getFloat(self, column_index: int) -> float: if self._window and self._pos >= 0: return self._window.getDouble(self._pos, column_index) return 0.0 def getDouble(self, column_index: int) -> float: if self._window and self._pos >= 0: return self._window.getDouble(self._pos, column_index) return 0.0 def getBlob(self, column_index: int) -> Optional[bytes]: if self._window and self._pos >= 0: return self._window.getBlob(self._pos, column_index) return None def isNull(self, column_index: int) -> bool: if self._window and self._pos >= 0: return self._window.isNull(self._pos, column_index) return True # ═══════════════════════════════════════════════════════════════ # CursorWrapper # ═══════════════════════════════════════════════════════════════ class CursorWrapper(Cursor): """CursorWrapper — 游标包装器""" def __init__(self, cursor: Cursor): super().__init__() self._wrapped_cursor = cursor def getWrappedCursor(self) -> Cursor: return self._wrapped_cursor def getCount(self) -> int: return self._wrapped_cursor.getCount() def getPosition(self) -> int: return self._wrapped_cursor.getPosition() def moveToFirst(self) -> bool: return self._wrapped_cursor.moveToFirst() def moveToLast(self) -> bool: return self._wrapped_cursor.moveToLast() def moveToNext(self) -> bool: return self._wrapped_cursor.moveToNext() def moveToPrevious(self) -> bool: return self._wrapped_cursor.moveToPrevious() def move(self, offset: int) -> bool: return self._wrapped_cursor.move(offset) def moveToPosition(self, position: int) -> bool: return self._wrapped_cursor.moveToPosition(position) def getColumnNames(self) -> List[str]: return self._wrapped_cursor.getColumnNames() def getColumnIndex(self, column_name: str) -> int: return self._wrapped_cursor.getColumnIndex(column_name) def getColumnIndexOrThrow(self, column_name: str) -> int: return self._wrapped_cursor.getColumnIndexOrThrow(column_name) def getString(self, column_index: int) -> Optional[str]: return self._wrapped_cursor.getString(column_index) def getInt(self, column_index: int) -> int: return self._wrapped_cursor.getInt(column_index) def getLong(self, column_index: int) -> int: return self._wrapped_cursor.getLong(column_index) def getFloat(self, column_index: int) -> float: return self._wrapped_cursor.getFloat(column_index) def getDouble(self, column_index: int) -> float: return self._wrapped_cursor.getDouble(column_index) def getShort(self, column_index: int) -> int: return self._wrapped_cursor.getShort(column_index) def getBlob(self, column_index: int) -> Optional[bytes]: return self._wrapped_cursor.getBlob(column_index) def close(self): self._wrapped_cursor.close() def isClosed(self) -> bool: return self._wrapped_cursor.isClosed() def registerContentObserver(self, observer: ContentObserver): self._wrapped_cursor.registerContentObserver(observer) def unregisterContentObserver(self, observer: ContentObserver): self._wrapped_cursor.unregisterContentObserver(observer) def registerDataSetObserver(self, observer: DataSetObserver): self._wrapped_cursor.registerDataSetObserver(observer) def unregisterDataSetObserver(self, observer: DataSetObserver): self._wrapped_cursor.unregisterDataSetObserver(observer) def getWantsAllOnMoveCalls(self) -> bool: return self._wrapped_cursor.getWantsAllOnMoveCalls() # ═══════════════════════════════════════════════════════════════ # CrossProcessCursor / CrossProcessCursorWrapper # ═══════════════════════════════════════════════════════════════ class CrossProcessCursor(Cursor): """CrossProcessCursor — 跨进程游标""" def fillWindow(self, pos: int, window: CursorWindow): pass def getWindow(self) -> Optional[CursorWindow]: return None def onMove(self, old_position: int, new_position: int) -> bool: return True class CrossProcessCursorWrapper(CursorWrapper): """CrossProcessCursorWrapper — 跨进程游标包装器""" def __init__(self, cursor: Cursor): super().__init__(cursor) self._cursor = cursor def fillWindow(self, pos: int, window: CursorWindow): if isinstance(self._cursor, CrossProcessCursor): self._cursor.fillWindow(pos, window) def getWindow(self) -> Optional[CursorWindow]: if isinstance(self._cursor, CrossProcessCursor): return self._cursor.getWindow() return None def onMove(self, old_position: int, new_position: int) -> bool: if isinstance(self._cursor, CrossProcessCursor): return self._cursor.onMove(old_position, new_position) return True # ═══════════════════════════════════════════════════════════════ # MatrixCursor # ═══════════════════════════════════════════════════════════════ class MatrixCursor(AbstractCursor): """MatrixCursor — 内存矩阵游标""" class RowBuilder(Stub): """RowBuilder — 行构建器""" def __init__(self, cursor: "MatrixCursor", row_index: int): super().__init__() self._cursor = cursor self._row_index = row_index def add(self, column_value: Any) -> "MatrixCursor.RowBuilder": self._cursor._add_row_value(self._row_index, column_value) return self def __init__(self, column_names: List[str], initial_capacity: int = 0): super().__init__() self._column_names = list(column_names) self._rows: List[List[Optional[Any]]] = [] self._num_columns = len(column_names) def _add_row_value(self, row_index: int, value: Any): while len(self._rows) <= row_index: self._rows.append([]) self._rows[row_index].append(value) def addRow(self, values: Union[List[Any], Tuple[Any, ...]]) -> None: row = list(values) + [None] * (self._num_columns - len(values)) self._rows.append(row[:self._num_columns]) def newRow(self) -> RowBuilder: row_index = len(self._rows) self._rows.append([]) return MatrixCursor.RowBuilder(self, row_index) def getCount(self) -> int: return len(self._rows) def getColumnNames(self) -> List[str]: return list(self._column_names) def getString(self, column_index: int) -> Optional[str]: if self._pos < 0 or self._pos >= len(self._rows): return None if column_index < 0 or column_index >= len(self._rows[self._pos]): return None v = self._rows[self._pos][column_index] return str(v) if v is not None else None def getInt(self, column_index: int) -> int: v = self.getString(column_index) if v is None: return 0 try: return int(float(v)) except (ValueError, TypeError): return 0 def getLong(self, column_index: int) -> int: return self.getInt(column_index) def getShort(self, column_index: int) -> int: return self.getInt(column_index) def getFloat(self, column_index: int) -> float: v = self.getString(column_index) if v is None: return 0.0 try: return float(v) except (ValueError, TypeError): return 0.0 def getDouble(self, column_index: int) -> float: return self.getFloat(column_index) def getBlob(self, column_index: int) -> Optional[bytes]: if self._pos < 0 or self._pos >= len(self._rows): return None if column_index < 0 or column_index >= len(self._rows[self._pos]): return None v = self._rows[self._pos][column_index] if isinstance(v, bytes): return v if isinstance(v, str): return v.encode("utf-8") return None def getType(self, column_index: int) -> int: v = None if 0 <= self._pos < len(self._rows): row = self._rows[self._pos] if 0 <= column_index < len(row): v = row[column_index] if v is None: return Cursor.FIELD_TYPE_NULL if isinstance(v, int): return Cursor.FIELD_TYPE_INTEGER if isinstance(v, float): return Cursor.FIELD_TYPE_FLOAT if isinstance(v, bytes): return Cursor.FIELD_TYPE_BLOB return Cursor.FIELD_TYPE_STRING def isNull(self, column_index: int) -> bool: return self.getType(column_index) == Cursor.FIELD_TYPE_NULL def toString(self) -> str: return f"MatrixCursor(cols={self._column_names}, rows={len(self._rows)})" # ═══════════════════════════════════════════════════════════════ # MergeCursor # ═══════════════════════════════════════════════════════════════ class MergeCursor(AbstractCursor): """MergeCursor — 合并多个游标""" def __init__(self, cursors: List[Cursor]): super().__init__() self._cursors = cursors self._cursor_index = 0 self._current_cursor: Optional[Cursor] = None def getCount(self) -> int: return sum(c.getCount() for c in self._cursors) def getColumnNames(self) -> List[str]: if self._cursors: return self._cursors[0].getColumnNames() return [] def moveToPosition(self, position: int) -> bool: if position < -1: return False if position >= self.getCount(): return False offset = 0 for i, c in enumerate(self._cursors): count = c.getCount() if position < offset + count: self._cursor_index = i self._current_cursor = c return c.moveToPosition(position - offset) offset += count return False def getString(self, column_index: int) -> Optional[str]: if self._current_cursor and self._pos >= 0: return self._current_cursor.getString(column_index) return None def getInt(self, column_index: int) -> int: if self._current_cursor and self._pos >= 0: return self._current_cursor.getInt(column_index) return 0 def close(self): for c in self._cursors: c.close() super().close() # ═══════════════════════════════════════════════════════════════ # CursorJoiner # ═══════════════════════════════════════════════════════════════ class CursorJoiner(Stub): """CursorJoiner — 游标连接器""" class Result(Stub): BOTH = 0 LEFT = 1 RIGHT = 2 def __init__(self, cursor_a: Cursor, column_names_a: List[str], cursor_b: Cursor, column_names_b: List[str]): super().__init__() self._cursor_a = cursor_a self._cursor_b = cursor_b self._col_names_a = column_names_a self._col_names_b = column_names_b self._has_next = False def hasNext(self) -> bool: return self._has_next def next(self) -> int: return self.Result.BOTH # ═══════════════════════════════════════════════════════════════ # DatabaseUtils # ═══════════════════════════════════════════════════════════════ class DatabaseUtils(Stub): """DatabaseUtils — 数据库工具""" STATEMENT_ABORT = "ABORT" STATEMENT_FAIL = "FAIL" STATEMENT_IGNORE = "IGNORE" STATEMENT_REPLACE = "REPLACE" STATEMENT_ROLLBACK = "ROLLBACK" @staticmethod def cursorFillWindow(cursor: Cursor, pos: int, window: CursorWindow): cursor.fillWindow(pos, window) @staticmethod def cursorStringToContentValues(cursor: Cursor, field: str, values: Any): pass @staticmethod def cursorIntToContentValues(cursor: Cursor, field: str, values: Any): pass @staticmethod def cursorLongToContentValues(cursor: Cursor, field: str, values: Any): pass @staticmethod def cursorDoubleToContentValues(cursor: Cursor, field: str, values: Any): pass @staticmethod def cursorRowToContentValues(cursor: Cursor, values: Any): for col_name in cursor.getColumnNames(): idx = cursor.getColumnIndex(col_name) val = cursor.getString(idx) if hasattr(values, "put"): values.put(col_name, val) @staticmethod def dumpCursor(cursor: Cursor): col_names = cursor.getColumnNames() sys.stderr.write(f"Rows: {cursor.getCount()}\n") sys.stderr.write(f"Columns: {col_names}\n") @staticmethod def dumpCursorToString(cursor: Cursor) -> str: return f"Cursor(count={cursor.getCount()})" @staticmethod def bindObjectToProgram(program: Any, index: int, value: Any): pass @staticmethod def cursorToContentValues(cursor: Cursor, values: Any): DatabaseUtils.cursorRowToContentValues(cursor, values) @staticmethod def longForQuery(db: Any, query: str, args: Optional[List[str]] = None) -> int: return 0 @staticmethod def stringForQuery(db: Any, query: str, args: Optional[List[str]] = None) -> Optional[str]: return None @staticmethod def blobForQuery(db: Any, query: str, args: Optional[List[str]] = None) -> Optional[bytes]: return None @staticmethod def sqlEscapeString(value: str) -> str: return value.replace("'", "''") @staticmethod def appendEscapedSQLString(sb: Any, value: str): escaped = DatabaseUtils.sqlEscapeString(value) sb.append(f"'{escaped}'") @staticmethod def appendValueToSql(sb: Any, value: Any): if value is None: sb.append("NULL") elif isinstance(value, bool): sb.append("1" if value else "0") elif isinstance(value, (int, float)): sb.append(str(value)) else: DatabaseUtils.appendEscapedSQLString(sb, str(value)) @staticmethod def appendSelectionArgs(original_values: List[str], new_values: List[str]) -> List[str]: return list(original_values) + list(new_values) @staticmethod def getCollationKey(name: str) -> str: return name.lower() @staticmethod def getHexCollationKey(name: str) -> str: return name.lower().encode("utf-8").hex() @staticmethod def createDbFromSqlStatements(context: Any, db_name: str, db_version: int, sql_statements: str): from .database_sqlite import SQLiteDatabase db_path = os.path.join(_app_data_dir, "databases", db_name) os.makedirs(os.path.dirname(db_path), exist_ok=True) db = SQLiteDatabase.openOrCreateDatabase(db_path, None) for stmt in sql_statements.split(";"): stmt = stmt.strip() if stmt: try: db.execSQL(stmt) except Exception: pass return db # ═══════════════════════════════════════════════════════════════ # DatabaseUtilsInsertHelper # ═══════════════════════════════════════════════════════════════ class DatabaseUtilsInsertHelper(Stub): """DatabaseUtils.InsertHelper — 插入辅助类""" TABLE_INFO_PRAGMA_COLUMNNAME_INDEX = 1 def __init__(self, db: Any, table_name: str): super().__init__() self._db = db self._table_name = table_name def getTableName(self) -> str: return self._table_name def prepareForInsert(self) -> int: return 0 def prepareForReplace(self) -> int: return 0 def bind(self, index: int, value: Any): pass def execute(self) -> int: return 0 def close(self): pass # ═══════════════════════════════════════════════════════════════ # Helper # ═══════════════════════════════════════════════════════════════ class IllegalArgumentException(RuntimeError): """IllegalArgumentException — 非法参数异常""" pass # ═══════════════════════════════════════════════════════════════ # __all__ # ═══════════════════════════════════════════════════════════════ __all__ = [ "Cursor", "MatrixCursor", "AbstractCursor", "AbstractWindowedCursor", "ContentObservable", "ContentObserver", "DataSetObservable", "DatabaseErrorHandler", "DatabaseUtils", "DefaultDatabaseErrorHandler", "CharArrayBuffer", "CursorJoiner", "CursorWindow", "CursorWrapper", "CrossProcessCursor", "CrossProcessCursorWrapper", "DataSetObserver", "MergeCursor", "SQLException", "CursorIndexOutOfBoundsException", "DatabaseUtilsInsertHelper", ]