mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-11-01 02:49:42 +01:00
LCL-Android-Sqlite: Changes the architecture to recreate the handle on each routine because it was crashing. Reorders the methods to be according to the interface. Fixes many issues and crashes and bugs in the Android-Sqlite dataset code.
git-svn-id: trunk@39503 -
This commit is contained in:
parent
bc13aee2de
commit
43740984e2
@ -10,7 +10,7 @@ object formSqlite: TformSqlite
|
|||||||
object btnConnect: TButton
|
object btnConnect: TButton
|
||||||
Left = 8
|
Left = 8
|
||||||
Height = 25
|
Height = 25
|
||||||
Top = 24
|
Top = 40
|
||||||
Width = 112
|
Width = 112
|
||||||
Caption = 'Connect'
|
Caption = 'Connect'
|
||||||
OnClick = btnConnectClick
|
OnClick = btnConnectClick
|
||||||
@ -19,7 +19,7 @@ object formSqlite: TformSqlite
|
|||||||
object DBEdit1: TDBEdit
|
object DBEdit1: TDBEdit
|
||||||
Left = 8
|
Left = 8
|
||||||
Height = 23
|
Height = 23
|
||||||
Top = 88
|
Top = 104
|
||||||
Width = 80
|
Width = 80
|
||||||
DataField = 'FirstFieldStr'
|
DataField = 'FirstFieldStr'
|
||||||
DataSource = SqliteDatasource
|
DataSource = SqliteDatasource
|
||||||
@ -30,7 +30,7 @@ object formSqlite: TformSqlite
|
|||||||
object DBNavigator1: TDBNavigator
|
object DBNavigator1: TDBNavigator
|
||||||
Left = 8
|
Left = 8
|
||||||
Height = 25
|
Height = 25
|
||||||
Top = 56
|
Top = 72
|
||||||
Width = 241
|
Width = 241
|
||||||
BevelOuter = bvNone
|
BevelOuter = bvNone
|
||||||
ChildSizing.EnlargeHorizontal = crsScaleChilds
|
ChildSizing.EnlargeHorizontal = crsScaleChilds
|
||||||
@ -48,7 +48,7 @@ object formSqlite: TformSqlite
|
|||||||
object btnCreateDB: TButton
|
object btnCreateDB: TButton
|
||||||
Left = 136
|
Left = 136
|
||||||
Height = 25
|
Height = 25
|
||||||
Top = 24
|
Top = 8
|
||||||
Width = 113
|
Width = 113
|
||||||
Caption = 'Create DB'
|
Caption = 'Create DB'
|
||||||
OnClick = btnCreateDBClick
|
OnClick = btnCreateDBClick
|
||||||
@ -57,14 +57,23 @@ object formSqlite: TformSqlite
|
|||||||
object Edit1: TEdit
|
object Edit1: TEdit
|
||||||
Left = 8
|
Left = 8
|
||||||
Height = 23
|
Height = 23
|
||||||
Top = 136
|
Top = 152
|
||||||
Width = 80
|
Width = 80
|
||||||
ReadOnly = True
|
ReadOnly = True
|
||||||
TabOrder = 4
|
TabOrder = 4
|
||||||
Text = 'Edit1'
|
Text = 'Edit1'
|
||||||
end
|
end
|
||||||
|
object btnSaveDB: TButton
|
||||||
|
Left = 137
|
||||||
|
Height = 25
|
||||||
|
Top = 40
|
||||||
|
Width = 112
|
||||||
|
Caption = 'Save DB'
|
||||||
|
OnClick = btnSaveDBClick
|
||||||
|
TabOrder = 5
|
||||||
|
end
|
||||||
object SqliteDatasource: TDatasource
|
object SqliteDatasource: TDatasource
|
||||||
left = 136
|
left = 136
|
||||||
top = 88
|
top = 104
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -6,7 +6,13 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, db, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
|
Classes, SysUtils, db, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
|
||||||
DbCtrls;
|
DbCtrls
|
||||||
|
{$ifdef CPUARM}
|
||||||
|
,sqlitejniandroid
|
||||||
|
{$else}
|
||||||
|
,sqlite3ds
|
||||||
|
{$endif}
|
||||||
|
;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -14,6 +20,7 @@ type
|
|||||||
|
|
||||||
TformSqlite = class(TForm)
|
TformSqlite = class(TForm)
|
||||||
btnConnect: TButton;
|
btnConnect: TButton;
|
||||||
|
btnSaveDB: TButton;
|
||||||
btnCreateDB: TButton;
|
btnCreateDB: TButton;
|
||||||
Edit1: TEdit;
|
Edit1: TEdit;
|
||||||
SqliteDatasource: TDatasource;
|
SqliteDatasource: TDatasource;
|
||||||
@ -21,10 +28,12 @@ type
|
|||||||
DBNavigator1: TDBNavigator;
|
DBNavigator1: TDBNavigator;
|
||||||
procedure btnCreateDBClick(Sender: TObject);
|
procedure btnCreateDBClick(Sender: TObject);
|
||||||
procedure btnConnectClick(Sender: TObject);
|
procedure btnConnectClick(Sender: TObject);
|
||||||
|
procedure btnSaveDBClick(Sender: TObject);
|
||||||
private
|
private
|
||||||
{ private declarations }
|
{ private declarations }
|
||||||
public
|
public
|
||||||
{ public declarations }
|
{ public declarations }
|
||||||
|
sqlitedb: {$ifdef CPUARM}TSqliteJNIDataset;{$else}TSqlite3Dataset;{$endif}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -32,19 +41,11 @@ var
|
|||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
{$ifdef CPUARM}
|
|
||||||
uses sqlitejniandroid;
|
|
||||||
{$else}
|
|
||||||
uses sqlite3ds;
|
|
||||||
{$endif}
|
|
||||||
|
|
||||||
{$R *.lfm}
|
{$R *.lfm}
|
||||||
|
|
||||||
{ TformSqlite }
|
{ TformSqlite }
|
||||||
|
|
||||||
procedure TformSqlite.btnConnectClick(Sender: TObject);
|
procedure TformSqlite.btnConnectClick(Sender: TObject);
|
||||||
var
|
|
||||||
sqlitedb: {$ifdef CPUARM}TSqliteJNIDataset;{$else}TSqlite3Dataset;{$endif}
|
|
||||||
begin
|
begin
|
||||||
{$ifdef CPUARM}
|
{$ifdef CPUARM}
|
||||||
sqlitedb := TSqliteJNIDataset.Create(Self);
|
sqlitedb := TSqliteJNIDataset.Create(Self);
|
||||||
@ -61,23 +62,30 @@ begin
|
|||||||
SqliteDatasource.DataSet := sqlitedb;
|
SqliteDatasource.DataSet := sqlitedb;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TformSqlite.btnSaveDBClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
if sqlitedb = nil then Exit;
|
||||||
|
sqlitedb.Close();
|
||||||
|
sqlitedb.Open();
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TformSqlite.btnCreateDBClick(Sender: TObject);
|
procedure TformSqlite.btnCreateDBClick(Sender: TObject);
|
||||||
var
|
var
|
||||||
sqlitedb: {$ifdef CPUARM}TSqliteJNIDataset;{$else}TSqlite3Dataset;{$endif}
|
lsqlitedb: {$ifdef CPUARM}TSqliteJNIDataset;{$else}TSqlite3Dataset;{$endif}
|
||||||
begin
|
begin
|
||||||
{$ifdef CPUARM}
|
{$ifdef CPUARM}
|
||||||
sqlitedb := TSqliteJNIDataset.Create(Self);
|
lsqlitedb := TSqliteJNIDataset.Create(Self);
|
||||||
sqlitedb.FileName := '/sdcard/database.db';
|
lsqlitedb.FileName := '/sdcard/database.db';
|
||||||
{$else}
|
{$else}
|
||||||
sqlitedb := TSqlite3Dataset.Create(Self);
|
lsqlitedb := TSqlite3Dataset.Create(Self);
|
||||||
sqlitedb.FileName := 'database.db';
|
lsqlitedb.FileName := 'database.db';
|
||||||
{$endif}
|
{$endif}
|
||||||
//SqliteDatasource.DataSet := sqlitedb;
|
//SqliteDatasource.DataSet := sqlitedb;
|
||||||
sqlitedb.TableName := 'TestTable';
|
lsqlitedb.TableName := 'TestTable';
|
||||||
sqlitedb.FieldDefs.Add('FirstFieldStr', ftString);
|
lsqlitedb.FieldDefs.Add('FirstFieldStr', ftString);
|
||||||
sqlitedb.FieldDefs.Add('SecondFieldInt', ftInteger);
|
lsqlitedb.FieldDefs.Add('SecondFieldInt', ftInteger);
|
||||||
sqlitedb.CreateTable();
|
lsqlitedb.CreateTable();
|
||||||
sqlitedb.Free;
|
lsqlitedb.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
@ -51,10 +51,11 @@ type
|
|||||||
// Java Classes
|
// Java Classes
|
||||||
FSqliteClosableClass, FSQLiteDatabaseClass, FDBCursorClass: JClass;
|
FSqliteClosableClass, FSQLiteDatabaseClass, FDBCursorClass: JClass;
|
||||||
// Java Methods
|
// Java Methods
|
||||||
FSqliteClosable_releaseReference: JMethodID;
|
FSqliteClosable_acquireReference, FSqliteClosable_releaseReference: JMethodID;
|
||||||
FSqliteDatabase_ExecSQLMethod, FSqliteDatabase_openOrCreateDatabase,
|
FSqliteDatabase_ExecSQLMethod, FSqliteDatabase_openOrCreateDatabase,
|
||||||
FSqliteDatabase_getVersion, FSqliteDatabase_query,
|
FSqliteDatabase_getVersion, FSqliteDatabase_query,
|
||||||
FSqliteDatabase_execSQL, FSqliteDatabase_rawQuery: JMethodID;
|
FSqliteDatabase_execSQL, FSqliteDatabase_rawQuery,
|
||||||
|
FSqliteDatabase_inTransaction: JMethodID;
|
||||||
FDBCursor_getColumnCount, FDBCursor_getColumnName, FDBCursor_getType,
|
FDBCursor_getColumnCount, FDBCursor_getColumnName, FDBCursor_getType,
|
||||||
FDBCursor_close, FDBCursor_getCount, FDBCursor_getDouble,
|
FDBCursor_close, FDBCursor_getCount, FDBCursor_getDouble,
|
||||||
FDBCursor_getLong, FDBCursor_getPosition, FDBCursor_getString,
|
FDBCursor_getLong, FDBCursor_getPosition, FDBCursor_getString,
|
||||||
@ -62,20 +63,23 @@ type
|
|||||||
FDBCursor_moveToPrevious: JMethodID;
|
FDBCursor_moveToPrevious: JMethodID;
|
||||||
// Java Objects
|
// Java Objects
|
||||||
AndroidDB: jobject; // SQLiteDatabase
|
AndroidDB: jobject; // SQLiteDatabase
|
||||||
|
function SplitSQLStatements(AInput: string): TStrings;
|
||||||
procedure FindJavaClassesAndMethods;
|
procedure FindJavaClassesAndMethods;
|
||||||
|
procedure RealInternalCloseHandle;
|
||||||
|
function RealInternalGetHandle: Pointer;
|
||||||
protected
|
protected
|
||||||
procedure BuildLinkedList; override;
|
procedure BuildLinkedList; override;
|
||||||
function GetLastInsertRowId: Int64; override;
|
function GetLastInsertRowId: Int64; override;
|
||||||
function GetRowsAffected:Integer; override;
|
function GetRowsAffected:Integer; override;
|
||||||
procedure InternalCloseHandle; override;
|
procedure InternalCloseHandle; override;
|
||||||
function InternalGetHandle: Pointer; override;
|
function InternalGetHandle: Pointer; override;
|
||||||
procedure RetrieveFieldDefs; override;
|
procedure RetrieveFieldDefs; override;
|
||||||
function SqliteExec(ASQL: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer; override;
|
function SqliteExec(ASQL: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer; override;
|
||||||
procedure PrepareReturnString;
|
procedure PrepareReturnString;
|
||||||
public
|
public
|
||||||
procedure ExecuteDirect(const ASQL: String); override;
|
procedure ExecuteDirect(const ASQL: String); override;
|
||||||
function QuickQuery(const ASQL: String; const AStrList: TStrings; FillObjects: Boolean): String; override;
|
function QuickQuery(const ASQL: String; const AStrList: TStrings; FillObjects: Boolean): String; override;
|
||||||
function ReturnString: String; override;
|
function ReturnString: String; override;
|
||||||
class function SqliteVersion: String; override;
|
class function SqliteVersion: String; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -160,76 +164,139 @@ end;
|
|||||||
|
|
||||||
{ TSqlite3Dataset }
|
{ TSqlite3Dataset }
|
||||||
|
|
||||||
function TSqliteJNIDataset.SqliteExec(ASQL: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer;
|
function TSqliteJNIDataset.SplitSQLStatements(AInput: string): TStrings;
|
||||||
var
|
var
|
||||||
// array for the parameters
|
i: Integer;
|
||||||
lParams: array[0..2] of JValue;
|
CurChar: Char;
|
||||||
lJavaString: JString;
|
CurStr: string;
|
||||||
|
InQuote: Boolean = false;
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.SqliteExec] ' + StrPas(ASQL));
|
Result := TStringList.Create;
|
||||||
|
for i := 1 to Length(AInput) do
|
||||||
// void execSQL(String sql)
|
|
||||||
// preparations
|
|
||||||
lJavaString :=javaEnvRef^^.NewStringUTF(javaEnvRef, ASQL);
|
|
||||||
lParams[0].l := lJavaString;
|
|
||||||
|
|
||||||
// Call the method
|
|
||||||
javaEnvRef^^.CallVoidMethodA(javaEnvRef, AndroidDB, FSqliteDatabase_execSQL, @lParams[0]);
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TSqliteJNIDataset.PrepareReturnString;
|
|
||||||
var
|
|
||||||
exceptionObj: jthrowable;
|
|
||||||
javaLangClass: jclass;
|
|
||||||
javaLangClass_getName: JMethodID;
|
|
||||||
lJavaString: JString;
|
|
||||||
lNativeString: PChar;
|
|
||||||
begin
|
|
||||||
FReturnString := '';
|
|
||||||
// There seams to be no way to get any information about the exception in JNI =(
|
|
||||||
//DebugLn('[TSqliteJNIDataset.PrepareReturnString] START');
|
|
||||||
|
|
||||||
if javaEnvRef^^.ExceptionCheck(javaEnvRef) = JNI_FALSE then
|
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.PrepareReturnString] No exception found');
|
CurChar := AInput[i];
|
||||||
Exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
exceptionObj := javaEnvRef^^.ExceptionOccurred(javaEnvRef);
|
if CurChar = '''' then InQuote := not InQuote;
|
||||||
javaEnvRef^^.ExceptionDescribe(javaEnvRef);
|
|
||||||
javaEnvRef^^.ExceptionClear(javaEnvRef);
|
// Add a statement when it finishes with a ; outside quotes
|
||||||
{DebugLn('[TSqliteJNIDataset.PrepareReturnString] A exceptionObj='+IntToHex(Cardinal(exceptionObj), 8));
|
if (CurChar = ';') and (not InQuote) then
|
||||||
javaLangClass := javaEnvRef^^.FindClass(javaEnvRef, 'java/lang/Class');
|
begin
|
||||||
javaLangClass_getName := javaEnvRef^^.GetMethodID(javaEnvRef, javaLangClass, 'getName', '()Ljava/lang/String;');
|
Result.Add(CurStr);
|
||||||
DebugLn('[TSqliteJNIDataset.PrepareReturnString] B');
|
CurStr := '';
|
||||||
lJavaString := javaEnvRef^^.CallObjectMethod(javaEnvRef, exceptionObj, javaLangClass_getName); // <--- crashes here
|
end
|
||||||
DebugLn('[TSqliteJNIDataset.PrepareReturnString] C lJavaString='+IntToHex(Cardinal(lJavaString), 8));
|
else
|
||||||
lNativeString := javaEnvRef^^.GetStringUTFChars(javaEnvRef, lJavaString, nil);
|
CurStr := CurStr + CurChar;
|
||||||
DebugLn('[TSqliteJNIDataset.PrepareReturnString] D');
|
end;
|
||||||
FReturnString := StrPas(lNativeString);
|
// add the last statement even if it doesn't end with a ;
|
||||||
javaEnvRef^^.ReleaseStringUTFChars(javaEnvRef, lJavaString, lNativeString);
|
if CurStr <> '' then Result.Add(CurStr);
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, exceptionObj);
|
|
||||||
DebugLn('[TSqliteJNIDataset.PrepareReturnString] FReturnString=' + FReturnString);}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSqliteJNIDataset.InternalCloseHandle;
|
procedure TSqliteJNIDataset.FindJavaClassesAndMethods;
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.InternalCloseHandle]');
|
FSQLiteDatabaseClass := javaEnvRef^^.FindClass(javaEnvRef, 'android/database/sqlite/SQLiteDatabase');
|
||||||
|
FSQLiteClosableClass := javaEnvRef^^.FindClass(javaEnvRef, 'android/database/sqlite/SQLiteClosable');
|
||||||
|
FDBCursorClass := javaEnvRef^^.FindClass(javaEnvRef, 'android/database/Cursor');
|
||||||
|
|
||||||
|
//
|
||||||
|
// Methods from SqliteDatabase
|
||||||
|
//
|
||||||
|
FSqliteDatabase_ExecSQLMethod := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'execSQL',
|
||||||
|
'(Ljava/lang/String;)V');
|
||||||
|
FSqliteDatabase_openOrCreateDatabase := javaEnvRef^^.GetStaticMethodID(javaEnvRef, FSQLiteDatabaseClass, 'openOrCreateDatabase',
|
||||||
|
'(Ljava/lang/String;Landroid/database/sqlite/SQLiteDatabase$CursorFactory;)Landroid/database/sqlite/SQLiteDatabase;');
|
||||||
|
//DebugLn('[TSqliteJNIDataset.FindJavaClassesAndMethods] FSqliteDatabase_openOrCreateDatabase='+IntToHex(Cardinal(FSqliteDatabase_openOrCreateDatabase), 8));
|
||||||
|
FSqliteDatabase_getVersion := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'getVersion',
|
||||||
|
'()I');
|
||||||
|
// public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
|
||||||
|
FSqliteDatabase_query := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'query',
|
||||||
|
'(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;');
|
||||||
|
// void execSQL(String sql)
|
||||||
|
FSqliteDatabase_execSQL := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'execSQL',
|
||||||
|
'(Ljava/lang/String;)V');
|
||||||
|
// public Cursor rawQuery (String sql, String[] selectionArgs)
|
||||||
|
FSqliteDatabase_rawQuery := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'rawQuery',
|
||||||
|
'(Ljava/lang/String;[Ljava/lang/String;)Landroid/database/Cursor;');
|
||||||
|
// public boolean inTransaction ()
|
||||||
|
FSqliteDatabase_inTransaction := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'inTransaction',
|
||||||
|
'()Z');
|
||||||
|
//
|
||||||
|
// Methods from FDBClosable
|
||||||
|
//
|
||||||
|
FSqliteClosable_acquireReference := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteClosableClass, 'acquireReference',
|
||||||
|
'()V');
|
||||||
|
FSqliteClosable_releaseReference := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteClosableClass, 'releaseReference',
|
||||||
|
'()V');
|
||||||
|
//
|
||||||
|
// Methods from FDBCursor
|
||||||
|
//
|
||||||
|
FDBCursor_getColumnCount := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getColumnCount',
|
||||||
|
'()I');
|
||||||
|
// abstract String getColumnName(int columnIndex)
|
||||||
|
FDBCursor_getColumnName := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getColumnName',
|
||||||
|
'(I)Ljava/lang/String;');
|
||||||
|
// public abstract int getType (int columnIndex) // Added in API level 11
|
||||||
|
if android_os_Build_VERSION_SDK_INT >= 11 then
|
||||||
|
begin
|
||||||
|
FDBCursor_getType := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getType',
|
||||||
|
'(I)I');
|
||||||
|
end;
|
||||||
|
// abstract void close()
|
||||||
|
FDBCursor_close := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'close',
|
||||||
|
'()V');
|
||||||
|
// public abstract int getCount ()
|
||||||
|
FDBCursor_getCount := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getCount',
|
||||||
|
'()I');
|
||||||
|
// public abstract double getDouble (int columnIndex)
|
||||||
|
FDBCursor_getDouble := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getDouble',
|
||||||
|
'(I)D');
|
||||||
|
// public abstract long getLong (int columnIndex)
|
||||||
|
FDBCursor_getLong := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getLong',
|
||||||
|
'(I)J');
|
||||||
|
// public abstract int getPosition ()
|
||||||
|
FDBCursor_getPosition := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getPosition',
|
||||||
|
'()I');
|
||||||
|
// public abstract String getString (int columnIndex)
|
||||||
|
FDBCursor_getString := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getString',
|
||||||
|
'(I)Ljava/lang/String;');
|
||||||
|
// public abstract boolean moveToFirst ()
|
||||||
|
FDBCursor_moveToFirst := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToFirst',
|
||||||
|
'()Z');
|
||||||
|
// public abstract boolean moveToNext ()
|
||||||
|
FDBCursor_moveToNext := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToNext',
|
||||||
|
'()Z');
|
||||||
|
// public abstract boolean moveToPosition (int position)
|
||||||
|
FDBCursor_moveToPosition := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToPosition',
|
||||||
|
'(I)Z');
|
||||||
|
// public abstract boolean moveToPrevious ()
|
||||||
|
FDBCursor_moveToPrevious := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToPrevious',
|
||||||
|
'()Z');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSqliteJNIDataset.RealInternalCloseHandle;
|
||||||
|
{var
|
||||||
|
inTransaction: Boolean;}
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.RealInternalCloseHandle]');
|
||||||
|
|
||||||
|
{// Before closing the reference, wait until the last transaction has finished
|
||||||
|
inTransaction := javaEnvRef^^.CallBooleanMethod(javaEnvRef, AndroidDB, FSqliteDatabase_inTransaction) = JNI_TRUE;
|
||||||
|
while inTransaction do
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.RealInternalCloseHandle] Waiting 100ms for the transaction to close');
|
||||||
|
Sleep(100);
|
||||||
|
inTransaction := javaEnvRef^^.CallBooleanMethod(javaEnvRef, AndroidDB, FSqliteDatabase_inTransaction) = JNI_TRUE;
|
||||||
|
end;}
|
||||||
|
|
||||||
// void android.database.sqlite.SQLiteClosable->releaseReference()
|
// void android.database.sqlite.SQLiteClosable->releaseReference()
|
||||||
javaEnvRef^^.CallVoidMethod(javaEnvRef, AndroidDB, FSqliteClosable_releaseReference);
|
javaEnvRef^^.CallVoidMethod(javaEnvRef, AndroidDB, FSqliteClosable_releaseReference);
|
||||||
//javaEnvRef^^.DeleteLocalRef(javaEnvRef, AndroidDB);
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, AndroidDB);
|
||||||
|
|
||||||
//f/sqlite3_close(FSqliteHandle);
|
//f/sqlite3_close(FSqliteHandle);
|
||||||
FSqliteHandle := nil;
|
FSqliteHandle := nil;
|
||||||
//todo:handle return data
|
//todo:handle return data
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSqliteJNIDataset.RealInternalGetHandle: Pointer;
|
||||||
function TSqliteJNIDataset.InternalGetHandle: Pointer;
|
|
||||||
const
|
const
|
||||||
CheckFileSql = 'Select Name from sqlite_master LIMIT 1';
|
CheckFileSql = 'Select Name from sqlite_master LIMIT 1';
|
||||||
var
|
var
|
||||||
@ -237,8 +304,7 @@ var
|
|||||||
lParams: array[0..2] of JValue;
|
lParams: array[0..2] of JValue;
|
||||||
lJavaString: JString;
|
lJavaString: JString;
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.InternalGetHandle]');
|
DebugLn('[TSqliteJNIDataset.RealInternalGetHandle]');
|
||||||
FindJavaClassesAndMethods();
|
|
||||||
|
|
||||||
// static SQLiteDatabase openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory)
|
// static SQLiteDatabase openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory)
|
||||||
// preparations
|
// preparations
|
||||||
@ -249,6 +315,133 @@ begin
|
|||||||
AndroidDB := javaEnvRef^^.CallStaticObjectMethodA(javaEnvRef, FSqliteDatabaseClass, FSqliteDatabase_openOrCreateDatabase, @lParams[0]);
|
AndroidDB := javaEnvRef^^.CallStaticObjectMethodA(javaEnvRef, FSqliteDatabaseClass, FSqliteDatabase_openOrCreateDatabase, @lParams[0]);
|
||||||
// clean up
|
// clean up
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
||||||
|
|
||||||
|
// Reinforce by getting a reference to the object, not only creating it -> Doesnt seam to make any difference
|
||||||
|
//javaEnvRef^^.CallVoidMethod(javaEnvRef, AndroidDB, FSqliteClosable_acquireReference);
|
||||||
|
Result := Pointer(AndroidDB);
|
||||||
|
DebugLn(Format('[TSqliteJNIDataset.RealInternalGetHandle] AndroidDB=%x', [PtrInt(AndroidDB)]));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSqliteJNIDataset.BuildLinkedList;
|
||||||
|
var
|
||||||
|
TempItem: PDataRecord;
|
||||||
|
Counter, ColumnCount, TrueRowCount: Integer;
|
||||||
|
lIsAfterLastRow: Boolean;
|
||||||
|
//
|
||||||
|
lJavaString: JString;
|
||||||
|
lNativeString: PChar;
|
||||||
|
dbCursor: JObject;
|
||||||
|
lParams: array[0..7] of JValue;
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.BuildLinkedList] FEffectiveSQL='+FEffectiveSQL);
|
||||||
|
RealInternalGetHandle();
|
||||||
|
{ //Get AutoInc Field initial value
|
||||||
|
if FAutoIncFieldNo <> -1 then
|
||||||
|
sqlite3_exec(FSqliteHandle, PChar('Select Max(' + FieldDefs[FAutoIncFieldNo].Name +
|
||||||
|
') from ' + FTableName), @GetAutoIncValue, @FNextAutoInc, nil);}
|
||||||
|
|
||||||
|
//FReturnCode := sqlite3_prepare(FSqliteHandle, PChar(FEffectiveSQL), -1, @vm, nil);
|
||||||
|
//if FReturnCode <> SQLITE_OK then
|
||||||
|
// DatabaseError(ReturnString, Self);
|
||||||
|
//
|
||||||
|
// public Cursor rawQuery (String sql, String[] selectionArgs)
|
||||||
|
lParams[0].l := javaEnvRef^^.NewStringUTF(javaEnvRef, PChar(FEffectiveSQL));
|
||||||
|
lParams[1].l := nil;
|
||||||
|
dbCursor := javaEnvRef^^.CallObjectMethodA(javaEnvRef, AndroidDB, FSqliteDatabase_rawQuery, @lParams[0]);
|
||||||
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lParams[0].l);
|
||||||
|
|
||||||
|
FDataAllocated := True;
|
||||||
|
|
||||||
|
TempItem := FBeginItem;
|
||||||
|
FRecordCount := 0;
|
||||||
|
ColumnCount := javaEnvRef^^.CallIntMethod(javaEnvRef, dbCursor, FDBCursor_getColumnCount);
|
||||||
|
TrueRowCount := javaEnvRef^^.CallIntMethod(javaEnvRef, dbCursor, FDBCursor_getCount);
|
||||||
|
FRowCount := ColumnCount;
|
||||||
|
//add extra rows for calculated fields
|
||||||
|
if FCalcFieldList <> nil then
|
||||||
|
Inc(FRowCount, FCalcFieldList.Count);
|
||||||
|
FRowBufferSize := (SizeOf(PPChar) * FRowCount);
|
||||||
|
//FReturnCode := sqlite3_step(vm);
|
||||||
|
//while FReturnCode = SQLITE_ROW do
|
||||||
|
//begin
|
||||||
|
//
|
||||||
|
// public abstract boolean moveToNext ()
|
||||||
|
DebugLn(Format('[TSqliteJNIDataset.BuildLinkedList] ColCount=%d RowCount=%d', [ColumnCount, TrueRowCount]));
|
||||||
|
if TrueRowCount > 0 then
|
||||||
|
begin
|
||||||
|
lIsAfterLastRow := javaEnvRef^^.CallBooleanMethod(javaEnvRef, dbCursor, FDBCursor_moveToNext) = JNI_FALSE;
|
||||||
|
while not lIsAfterLastRow do
|
||||||
|
begin
|
||||||
|
Inc(FRecordCount);
|
||||||
|
DebugLn(Format('[TSqliteJNIDataset.BuildLinkedList] reading row # %d', [FRecordCount]));
|
||||||
|
New(TempItem^.Next);
|
||||||
|
TempItem^.Next^.Previous := TempItem;
|
||||||
|
TempItem := TempItem^.Next;
|
||||||
|
GetMem(TempItem^.Row, FRowBufferSize);
|
||||||
|
for Counter := 0 to ColumnCount - 1 do
|
||||||
|
begin
|
||||||
|
// TempItem^.Row[Counter] := StrNew(sqlite3_column_text(vm, Counter));
|
||||||
|
// public abstract String getString (int columnIndex)
|
||||||
|
lParams[0].i := Counter;
|
||||||
|
lJavaString := javaEnvRef^^.CallObjectMethodA(javaEnvRef, dbCursor, FDBCursor_getString, @lParams[0]);
|
||||||
|
//DebugLn(Format('[TSqliteJNIDataset.BuildLinkedList] reading row # %d', [FRecordCount]));
|
||||||
|
lNativeString := javaEnvRef^^.GetStringUTFChars(javaEnvRef, lJavaString, nil);
|
||||||
|
TempItem^.Row[Counter] := StrNew(lNativeString);
|
||||||
|
javaEnvRef^^.ReleaseStringUTFChars(javaEnvRef, lJavaString, lNativeString);
|
||||||
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
||||||
|
end;
|
||||||
|
//initialize calculated fields with nil
|
||||||
|
for Counter := ColumnCount to FRowCount - 1 do
|
||||||
|
TempItem^.Row[Counter] := nil;
|
||||||
|
//FReturnCode := sqlite3_step(vm);
|
||||||
|
lIsAfterLastRow := javaEnvRef^^.CallBooleanMethod(javaEnvRef, dbCursor, FDBCursor_moveToNext) = JNI_FALSE;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
//sqlite3_finalize(vm);
|
||||||
|
javaEnvRef^^.CallVoidMethod(javaEnvRef, dbCursor, FDBCursor_close);
|
||||||
|
|
||||||
|
// Attach EndItem
|
||||||
|
TempItem^.Next := FEndItem;
|
||||||
|
FEndItem^.Previous := TempItem;
|
||||||
|
|
||||||
|
// Alloc temporary item used in append/insert
|
||||||
|
GetMem(FCacheItem^.Row, FRowBufferSize);
|
||||||
|
for Counter := 0 to FRowCount - 1 do
|
||||||
|
FCacheItem^.Row[Counter] := nil;
|
||||||
|
// Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
|
||||||
|
GetMem(FBeginItem^.Row, FRowBufferSize);
|
||||||
|
//Todo: see if is better to nullif using FillDWord
|
||||||
|
for Counter := 0 to FRowCount - 1 do
|
||||||
|
FBeginItem^.Row[Counter] := nil;
|
||||||
|
|
||||||
|
RealInternalCloseHandle();
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSqliteJNIDataset.GetLastInsertRowId: Int64;
|
||||||
|
begin
|
||||||
|
Result := FLastInsertRowId;
|
||||||
|
DebugLn('[TSqliteJNIDataset.GetLastInsertRowId] Result='+IntToStr(Result));
|
||||||
|
//f/Result := sqlite3_last_insert_rowid(FSqliteHandle);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSqliteJNIDataset.GetRowsAffected: Integer;
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.GetRowsAffected]');
|
||||||
|
//f/Result := sqlite3_changes(FSqliteHandle);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSqliteJNIDataset.InternalCloseHandle;
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.InternalCloseHandle]');
|
||||||
|
// RealInternalCloseHandle();
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TSqliteJNIDataset.InternalGetHandle: Pointer;
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.InternalGetHandle]');
|
||||||
|
FindJavaClassesAndMethods();
|
||||||
|
Result := Pointer($beef);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSqliteJNIDataset.RetrieveFieldDefs;
|
procedure TSqliteJNIDataset.RetrieveFieldDefs;
|
||||||
@ -265,6 +458,7 @@ var
|
|||||||
lParams: array[0..7] of JValue;
|
lParams: array[0..7] of JValue;
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.RetrieveFieldDefs]');
|
DebugLn('[TSqliteJNIDataset.RetrieveFieldDefs]');
|
||||||
|
RealInternalGetHandle();
|
||||||
FAutoIncFieldNo := -1;
|
FAutoIncFieldNo := -1;
|
||||||
FieldDefs.Clear;
|
FieldDefs.Clear;
|
||||||
|
|
||||||
@ -341,6 +535,9 @@ begin
|
|||||||
// but it throws a CursorIndexOutOfBoundsException if the cursor is in position -1
|
// but it throws a CursorIndexOutOfBoundsException if the cursor is in position -1
|
||||||
// so if the database has no rows, getType doesn't work o.O
|
// so if the database has no rows, getType doesn't work o.O
|
||||||
begin
|
begin
|
||||||
|
// getType won't work if we don't first move to a row
|
||||||
|
javaEnvRef^^.CallBooleanMethod(javaEnvRef, dbCursor, FDBCursor_moveToFirst);
|
||||||
|
|
||||||
// public abstract int getType (int columnIndex) // Added in API level 11
|
// public abstract int getType (int columnIndex) // Added in API level 11
|
||||||
lParams[0].i := i;
|
lParams[0].i := i;
|
||||||
lColumnType := javaEnvRef^^.CallIntMethodA(javaEnvRef, dbCursor, FDBCursor_getType, @lParams[0]);
|
lColumnType := javaEnvRef^^.CallIntMethodA(javaEnvRef, dbCursor, FDBCursor_getType, @lParams[0]);
|
||||||
@ -392,12 +589,90 @@ begin
|
|||||||
end;
|
end;
|
||||||
//sqlite3_finalize(vm);
|
//sqlite3_finalize(vm);
|
||||||
javaEnvRef^^.CallVoidMethod(javaEnvRef, dbCursor, FDBCursor_close);
|
javaEnvRef^^.CallVoidMethod(javaEnvRef, dbCursor, FDBCursor_close);
|
||||||
|
RealInternalCloseHandle();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSqliteJNIDataset.GetRowsAffected: Integer;
|
function TSqliteJNIDataset.SqliteExec(ASQL: PChar; ACallback: TSqliteCdeclCallback; Data: Pointer): Integer;
|
||||||
|
var
|
||||||
|
// array for the parameters
|
||||||
|
lParams: array[0..2] of JValue;
|
||||||
|
lJavaString: JString;
|
||||||
|
lSQLStrings: TStrings;
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.GetRowsAffected]');
|
DebugLn(Format('[TSqliteJNIDataset.SqliteExec] AndroidDB=%x ACallback=%x Data=%x ASQL=%s',
|
||||||
//f/Result := sqlite3_changes(FSqliteHandle);
|
[PtrInt(AndroidDB), PtrInt(ACallback), PtrInt(Data), StrPas(ASQL)]));
|
||||||
|
|
||||||
|
// If we don't renew our AndroidDB, we get crashes =/
|
||||||
|
//I/lclapp ( 901): [TSqliteJNIDataset.SqliteExec] AndroidDB=410A5178 ACallback=0 Data=0 ASQL=BEGIN;INSERT INTO TestTable (FirstFieldStr,SecondFieldInt) VALUES ('fe1
|
||||||
|
//I/lclapp ( 901): ',NULL);COMMIT;
|
||||||
|
// W/dalvikvm( 901): JNI WARNING: 0x410a5178 is not a valid JNI reference
|
||||||
|
// W/dalvikvm( 901): in Lcom/pascal/lcltest/LCLActivity;.LCLOnTouch:(FFI)I (CallVoidMethodA)
|
||||||
|
RealInternalGetHandle();
|
||||||
|
|
||||||
|
// Split the SQL and execute each part
|
||||||
|
lSQLStrings := SplitSQLStatements(StrPas(ASQL));
|
||||||
|
|
||||||
|
for i := 0 to lSQLStrings.Count-1 do
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.SqliteExec] Executing SQL part: ' + lSQLStrings.Strings[i]);
|
||||||
|
// void execSQL(String sql)
|
||||||
|
// preparations
|
||||||
|
lJavaString := javaEnvRef^^.NewStringUTF(javaEnvRef, PChar(lSQLStrings.Strings[i]));
|
||||||
|
lParams[0].l := lJavaString;
|
||||||
|
// Call the method
|
||||||
|
javaEnvRef^^.CallVoidMethodA(javaEnvRef, AndroidDB, FSqliteDatabase_execSQL, @lParams[0]);
|
||||||
|
// clean up
|
||||||
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Call the callback
|
||||||
|
if ACallback <> nil then
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.SqliteExec] Calling callback');
|
||||||
|
ACallback(Data, 0, nil, nil);
|
||||||
|
end;
|
||||||
|
|
||||||
|
RealInternalCloseHandle();
|
||||||
|
lSQLStrings.Free;
|
||||||
|
|
||||||
|
DebugLn('[TSqliteJNIDataset.SqliteExec] END');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSqliteJNIDataset.PrepareReturnString;
|
||||||
|
var
|
||||||
|
exceptionObj: jthrowable;
|
||||||
|
javaLangClass: jclass;
|
||||||
|
javaLangClass_getName: JMethodID;
|
||||||
|
lJavaString: JString;
|
||||||
|
lNativeString: PChar;
|
||||||
|
begin
|
||||||
|
FReturnString := '';
|
||||||
|
// There seams to be no way to get any information about the exception in JNI =(
|
||||||
|
//DebugLn('[TSqliteJNIDataset.PrepareReturnString] START');
|
||||||
|
|
||||||
|
if javaEnvRef^^.ExceptionCheck(javaEnvRef) = JNI_FALSE then
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.PrepareReturnString] No exception found');
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
exceptionObj := javaEnvRef^^.ExceptionOccurred(javaEnvRef);
|
||||||
|
javaEnvRef^^.ExceptionDescribe(javaEnvRef);
|
||||||
|
javaEnvRef^^.ExceptionClear(javaEnvRef);
|
||||||
|
{DebugLn('[TSqliteJNIDataset.PrepareReturnString] A exceptionObj='+IntToHex(Cardinal(exceptionObj), 8));
|
||||||
|
javaLangClass := javaEnvRef^^.FindClass(javaEnvRef, 'java/lang/Class');
|
||||||
|
javaLangClass_getName := javaEnvRef^^.GetMethodID(javaEnvRef, javaLangClass, 'getName', '()Ljava/lang/String;');
|
||||||
|
DebugLn('[TSqliteJNIDataset.PrepareReturnString] B');
|
||||||
|
lJavaString := javaEnvRef^^.CallObjectMethod(javaEnvRef, exceptionObj, javaLangClass_getName); // <--- crashes here
|
||||||
|
DebugLn('[TSqliteJNIDataset.PrepareReturnString] C lJavaString='+IntToHex(Cardinal(lJavaString), 8));
|
||||||
|
lNativeString := javaEnvRef^^.GetStringUTFChars(javaEnvRef, lJavaString, nil);
|
||||||
|
DebugLn('[TSqliteJNIDataset.PrepareReturnString] D');
|
||||||
|
FReturnString := StrPas(lNativeString);
|
||||||
|
javaEnvRef^^.ReleaseStringUTFChars(javaEnvRef, lJavaString, lNativeString);
|
||||||
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
||||||
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, exceptionObj);
|
||||||
|
DebugLn('[TSqliteJNIDataset.PrepareReturnString] FReturnString=' + FReturnString);}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSqliteJNIDataset.ExecuteDirect(const ASQL: String);
|
procedure TSqliteJNIDataset.ExecuteDirect(const ASQL: String);
|
||||||
@ -407,6 +682,7 @@ var
|
|||||||
lJavaString: JString;
|
lJavaString: JString;
|
||||||
begin
|
begin
|
||||||
DebugLn('[TSqliteJNIDataset.ExecuteDirect] ' + ASQL);
|
DebugLn('[TSqliteJNIDataset.ExecuteDirect] ' + ASQL);
|
||||||
|
RealInternalGetHandle();
|
||||||
|
|
||||||
// void execSQL(String sql)
|
// void execSQL(String sql)
|
||||||
// preparations
|
// preparations
|
||||||
@ -419,6 +695,8 @@ begin
|
|||||||
// clean up
|
// clean up
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
||||||
|
|
||||||
|
RealInternalCloseHandle();
|
||||||
|
|
||||||
{FReturnCode := sqlite3_prepare(FSqliteHandle, Pchar(ASQL), -1, @vm, nil);
|
{FReturnCode := sqlite3_prepare(FSqliteHandle, Pchar(ASQL), -1, @vm, nil);
|
||||||
if FReturnCode <> SQLITE_OK then
|
if FReturnCode <> SQLITE_OK then
|
||||||
DatabaseError(ReturnString, Self);
|
DatabaseError(ReturnString, Self);
|
||||||
@ -426,194 +704,6 @@ begin
|
|||||||
sqlite3_finalize(vm);}
|
sqlite3_finalize(vm);}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSqliteJNIDataset.FindJavaClassesAndMethods;
|
|
||||||
begin
|
|
||||||
FSQLiteDatabaseClass := javaEnvRef^^.FindClass(javaEnvRef, 'android/database/sqlite/SQLiteDatabase');
|
|
||||||
FSQLiteClosableClass := javaEnvRef^^.FindClass(javaEnvRef, 'android/database/sqlite/SQLiteClosable');
|
|
||||||
FDBCursorClass := javaEnvRef^^.FindClass(javaEnvRef, 'android/database/Cursor');
|
|
||||||
|
|
||||||
//
|
|
||||||
// Methods from SqliteDatabase
|
|
||||||
//
|
|
||||||
FSqliteDatabase_ExecSQLMethod := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'execSQL',
|
|
||||||
'(Ljava/lang/String;)V');
|
|
||||||
FSqliteDatabase_openOrCreateDatabase := javaEnvRef^^.GetStaticMethodID(javaEnvRef, FSQLiteDatabaseClass, 'openOrCreateDatabase',
|
|
||||||
'(Ljava/lang/String;Landroid/database/sqlite/SQLiteDatabase$CursorFactory;)Landroid/database/sqlite/SQLiteDatabase;');
|
|
||||||
//DebugLn('[TSqliteJNIDataset.FindJavaClassesAndMethods] FSqliteDatabase_openOrCreateDatabase='+IntToHex(Cardinal(FSqliteDatabase_openOrCreateDatabase), 8));
|
|
||||||
FSqliteDatabase_getVersion := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'getVersion',
|
|
||||||
'()I');
|
|
||||||
// public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
|
|
||||||
FSqliteDatabase_query := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'query',
|
|
||||||
'(Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;');
|
|
||||||
// void execSQL(String sql)
|
|
||||||
FSqliteDatabase_execSQL := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'execSQL',
|
|
||||||
'(Ljava/lang/String;)V');
|
|
||||||
// public Cursor rawQuery (String sql, String[] selectionArgs)
|
|
||||||
FSqliteDatabase_rawQuery := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteDatabaseClass, 'rawQuery',
|
|
||||||
'(Ljava/lang/String;[Ljava/lang/String;)Landroid/database/Cursor;');
|
|
||||||
//
|
|
||||||
// Methods from FDBClosable
|
|
||||||
//
|
|
||||||
FSqliteClosable_releaseReference := javaEnvRef^^.GetMethodID(javaEnvRef, FSQLiteClosableClass, 'releaseReference',
|
|
||||||
'()V');
|
|
||||||
//
|
|
||||||
// Methods from FDBCursor
|
|
||||||
//
|
|
||||||
FDBCursor_getColumnCount := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getColumnCount',
|
|
||||||
'()I');
|
|
||||||
// abstract String getColumnName(int columnIndex)
|
|
||||||
FDBCursor_getColumnName := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getColumnName',
|
|
||||||
'(I)Ljava/lang/String;');
|
|
||||||
// public abstract int getType (int columnIndex) // Added in API level 11
|
|
||||||
if android_os_Build_VERSION_SDK_INT >= 11 then
|
|
||||||
begin
|
|
||||||
FDBCursor_getType := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getType',
|
|
||||||
'(I)I');
|
|
||||||
end;
|
|
||||||
// abstract void close()
|
|
||||||
FDBCursor_close := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'close',
|
|
||||||
'()V');
|
|
||||||
// public abstract int getCount ()
|
|
||||||
FDBCursor_getCount := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getCount',
|
|
||||||
'()I');
|
|
||||||
// public abstract double getDouble (int columnIndex)
|
|
||||||
FDBCursor_getDouble := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getDouble',
|
|
||||||
'(I)D');
|
|
||||||
// public abstract long getLong (int columnIndex)
|
|
||||||
FDBCursor_getLong := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getLong',
|
|
||||||
'(I)J');
|
|
||||||
// public abstract int getPosition ()
|
|
||||||
FDBCursor_getPosition := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getPosition',
|
|
||||||
'()I');
|
|
||||||
// public abstract String getString (int columnIndex)
|
|
||||||
FDBCursor_getString := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'getString',
|
|
||||||
'(I)Ljava/lang/String;');
|
|
||||||
// public abstract boolean moveToFirst ()
|
|
||||||
FDBCursor_moveToFirst := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToFirst',
|
|
||||||
'()Z');
|
|
||||||
// public abstract boolean moveToNext ()
|
|
||||||
FDBCursor_moveToNext := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToNext',
|
|
||||||
'()Z');
|
|
||||||
// public abstract boolean moveToPosition (int position)
|
|
||||||
FDBCursor_moveToPosition := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToPosition',
|
|
||||||
'(I)Z');
|
|
||||||
// public abstract boolean moveToPrevious ()
|
|
||||||
FDBCursor_moveToPrevious := javaEnvRef^^.GetMethodID(javaEnvRef, FDBCursorClass, 'moveToPrevious',
|
|
||||||
'()Z');
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TSqliteJNIDataset.BuildLinkedList;
|
|
||||||
var
|
|
||||||
TempItem: PDataRecord;
|
|
||||||
Counter, ColumnCount, TrueRowCount: Integer;
|
|
||||||
lIsAfterLastRow: Boolean;
|
|
||||||
//
|
|
||||||
lJavaString: JString;
|
|
||||||
lNativeString: PChar;
|
|
||||||
dbCursor: JObject;
|
|
||||||
lParams: array[0..7] of JValue;
|
|
||||||
begin
|
|
||||||
DebugLn('[TSqliteJNIDataset.BuildLinkedList] FEffectiveSQL='+FEffectiveSQL);
|
|
||||||
{ //Get AutoInc Field initial value
|
|
||||||
if FAutoIncFieldNo <> -1 then
|
|
||||||
sqlite3_exec(FSqliteHandle, PChar('Select Max(' + FieldDefs[FAutoIncFieldNo].Name +
|
|
||||||
') from ' + FTableName), @GetAutoIncValue, @FNextAutoInc, nil);}
|
|
||||||
|
|
||||||
//FReturnCode := sqlite3_prepare(FSqliteHandle, PChar(FEffectiveSQL), -1, @vm, nil);
|
|
||||||
//if FReturnCode <> SQLITE_OK then
|
|
||||||
// DatabaseError(ReturnString, Self);
|
|
||||||
//
|
|
||||||
// public Cursor rawQuery (String sql, String[] selectionArgs)
|
|
||||||
lParams[0].l := javaEnvRef^^.NewStringUTF(javaEnvRef, PChar(FEffectiveSQL));
|
|
||||||
lParams[1].l := nil;
|
|
||||||
dbCursor := javaEnvRef^^.CallObjectMethodA(javaEnvRef, AndroidDB, FSqliteDatabase_rawQuery, @lParams[0]);
|
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lParams[0].l);
|
|
||||||
|
|
||||||
FDataAllocated := True;
|
|
||||||
|
|
||||||
TempItem := FBeginItem;
|
|
||||||
FRecordCount := 0;
|
|
||||||
ColumnCount := javaEnvRef^^.CallIntMethod(javaEnvRef, dbCursor, FDBCursor_getColumnCount);
|
|
||||||
TrueRowCount := javaEnvRef^^.CallIntMethod(javaEnvRef, dbCursor, FDBCursor_getCount);
|
|
||||||
FRowCount := ColumnCount;
|
|
||||||
//add extra rows for calculated fields
|
|
||||||
if FCalcFieldList <> nil then
|
|
||||||
Inc(FRowCount, FCalcFieldList.Count);
|
|
||||||
FRowBufferSize := (SizeOf(PPChar) * FRowCount);
|
|
||||||
//FReturnCode := sqlite3_step(vm);
|
|
||||||
//while FReturnCode = SQLITE_ROW do
|
|
||||||
//begin
|
|
||||||
//
|
|
||||||
// public abstract boolean moveToNext ()
|
|
||||||
DebugLn(Format('[TSqliteJNIDataset.BuildLinkedList] ColCount=%d RowCount=%d', [ColumnCount, TrueRowCount]));
|
|
||||||
if TrueRowCount > 0 then
|
|
||||||
begin
|
|
||||||
lIsAfterLastRow := javaEnvRef^^.CallBooleanMethod(javaEnvRef, dbCursor, FDBCursor_moveToNext) = JNI_TRUE;
|
|
||||||
while not lIsAfterLastRow do
|
|
||||||
begin
|
|
||||||
Inc(FRecordCount);
|
|
||||||
New(TempItem^.Next);
|
|
||||||
TempItem^.Next^.Previous := TempItem;
|
|
||||||
TempItem := TempItem^.Next;
|
|
||||||
GetMem(TempItem^.Row, FRowBufferSize);
|
|
||||||
for Counter := 0 to ColumnCount - 1 do
|
|
||||||
begin
|
|
||||||
// TempItem^.Row[Counter] := StrNew(sqlite3_column_text(vm, Counter));
|
|
||||||
// public abstract String getString (int columnIndex)
|
|
||||||
lJavaString := javaEnvRef^^.CallObjectMethod(javaEnvRef, dbCursor, FDBCursor_getString);
|
|
||||||
lNativeString := javaEnvRef^^.GetStringUTFChars(javaEnvRef, lJavaString, nil);
|
|
||||||
TempItem^.Row[Counter] := StrNew(lNativeString);
|
|
||||||
javaEnvRef^^.ReleaseStringUTFChars(javaEnvRef, lJavaString, lNativeString);
|
|
||||||
javaEnvRef^^.DeleteLocalRef(javaEnvRef, lJavaString);
|
|
||||||
end;
|
|
||||||
//initialize calculated fields with nil
|
|
||||||
for Counter := ColumnCount to FRowCount - 1 do
|
|
||||||
TempItem^.Row[Counter] := nil;
|
|
||||||
//FReturnCode := sqlite3_step(vm);
|
|
||||||
lIsAfterLastRow := javaEnvRef^^.CallBooleanMethod(javaEnvRef, dbCursor, FDBCursor_moveToNext) = JNI_TRUE;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
//sqlite3_finalize(vm);
|
|
||||||
javaEnvRef^^.CallVoidMethod(javaEnvRef, dbCursor, FDBCursor_close);
|
|
||||||
|
|
||||||
// Attach EndItem
|
|
||||||
TempItem^.Next := FEndItem;
|
|
||||||
FEndItem^.Previous := TempItem;
|
|
||||||
|
|
||||||
// Alloc temporary item used in append/insert
|
|
||||||
GetMem(FCacheItem^.Row, FRowBufferSize);
|
|
||||||
for Counter := 0 to FRowCount - 1 do
|
|
||||||
FCacheItem^.Row[Counter] := nil;
|
|
||||||
// Fill FBeginItem.Row with nil -> necessary for avoid exceptions in empty datasets
|
|
||||||
GetMem(FBeginItem^.Row, FRowBufferSize);
|
|
||||||
//Todo: see if is better to nullif using FillDWord
|
|
||||||
for Counter := 0 to FRowCount - 1 do
|
|
||||||
FBeginItem^.Row[Counter] := nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSqliteJNIDataset.GetLastInsertRowId: Int64;
|
|
||||||
begin
|
|
||||||
Result := FLastInsertRowId;
|
|
||||||
DebugLn('[TSqliteJNIDataset.GetLastInsertRowId] Result='+IntToStr(Result));
|
|
||||||
//f/Result := sqlite3_last_insert_rowid(FSqliteHandle);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSqliteJNIDataset.ReturnString: String;
|
|
||||||
begin
|
|
||||||
Result := FReturnString;
|
|
||||||
end;
|
|
||||||
|
|
||||||
class function TSqliteJNIDataset.SqliteVersion: String;
|
|
||||||
var
|
|
||||||
intVersion: JInt;
|
|
||||||
begin
|
|
||||||
DebugLn('[TSqliteJNIDataset.SqliteVersion]');
|
|
||||||
// public int getVersion ()
|
|
||||||
//intVersion := javaEnvRef^^.CallIntMethod(javaEnvRef, AndroidDB, FSqliteClosable_getVersion);
|
|
||||||
Result := '3.4'; // IntToStr(intVersion); cant access AndroidDB in class method
|
|
||||||
DebugLn('[TSqliteJNIDataset.SqliteVersion] Result='+Result);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSqliteJNIDataset.QuickQuery(const ASQL: String; const AStrList: TStrings; FillObjects:Boolean): String;
|
function TSqliteJNIDataset.QuickQuery(const ASQL: String; const AStrList: TStrings; FillObjects:Boolean): String;
|
||||||
var
|
var
|
||||||
vm: Pointer;
|
vm: Pointer;
|
||||||
@ -659,5 +749,21 @@ begin
|
|||||||
sqlite3_finalize(vm); }
|
sqlite3_finalize(vm); }
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSqliteJNIDataset.ReturnString: String;
|
||||||
|
begin
|
||||||
|
Result := FReturnString;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TSqliteJNIDataset.SqliteVersion: String;
|
||||||
|
var
|
||||||
|
intVersion: JInt;
|
||||||
|
begin
|
||||||
|
DebugLn('[TSqliteJNIDataset.SqliteVersion]');
|
||||||
|
// public int getVersion ()
|
||||||
|
//intVersion := javaEnvRef^^.CallIntMethod(javaEnvRef, AndroidDB, FSqliteClosable_getVersion);
|
||||||
|
Result := '3.4'; // IntToStr(intVersion); cant access AndroidDB in class method
|
||||||
|
DebugLn('[TSqliteJNIDataset.SqliteVersion] Result='+Result);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user