Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb77f67b

Browse files
rhermanklinkdpgeorge
authored andcommitted
unix-ffi/sqlite3: Add commit and rollback functionality like CPython.
To increase the similarity between this module and CPythons sqlite3 modulethe commit() and rollback() as defined in CPythons version have beenadded, along with the different (auto)commit behaviors present there.The defaults are also set to the same as in CPython, and can be changedwith the same parameters in connect(), as is showcased in the new test.Signed-off-by: Robert Klink <rhermanklink@ripe.net>
1 parent83598cd commitb77f67b

File tree

2 files changed

+137
-38
lines changed

2 files changed

+137
-38
lines changed

‎unix-ffi/sqlite3/sqlite3.py‎

Lines changed: 95 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
sqlite3_open=sq3.func("i","sqlite3_open","sp")
1313
# int sqlite3_config(int, ...);
1414
sqlite3_config=sq3.func("i","sqlite3_config","ii")
15+
# int sqlite3_get_autocommit(sqlite3*);
16+
sqlite3_get_autocommit=sq3.func("i","sqlite3_get_autocommit","p")
1517
# int sqlite3_close_v2(sqlite3*);
1618
sqlite3_close=sq3.func("i","sqlite3_close_v2","p")
1719
# int sqlite3_prepare(
@@ -57,6 +59,9 @@
5759

5860
SQLITE_CONFIG_URI=17
5961

62+
# For compatibility with CPython sqlite3 driver
63+
LEGACY_TRANSACTION_CONTROL=-1
64+
6065

6166
classError(Exception):
6267
pass
@@ -71,86 +76,138 @@ def get_ptr_size():
7176
returnuctypes.sizeof({"ptr": (0|uctypes.PTR,uctypes.PTR)})
7277

7378

79+
def__prepare_stmt(db,sql):
80+
# Prepares a statement
81+
stmt_ptr=bytes(get_ptr_size())
82+
res=sqlite3_prepare(db,sql,-1,stmt_ptr,None)
83+
check_error(db,res)
84+
returnint.from_bytes(stmt_ptr,sys.byteorder)
85+
86+
def__exec_stmt(db,sql):
87+
# Prepares, executes, and finalizes a statement
88+
stmt=__prepare_stmt(db,sql)
89+
sqlite3_step(stmt)
90+
res=sqlite3_finalize(stmt)
91+
check_error(db,res)
92+
93+
def__is_dml(sql):
94+
# Checks if a sql query is a DML, as these get a BEGIN in LEGACY_TRANSACTION_CONTROL
95+
fordmlin ["INSERT","DELETE","UPDATE","MERGE"]:
96+
ifdmlinsql.upper():
97+
returnTrue
98+
returnFalse
99+
100+
74101
classConnections:
75-
def__init__(self,h):
76-
self.h=h
102+
def__init__(self,db,isolation_level,autocommit):
103+
self.db=db
104+
self.isolation_level=isolation_level
105+
self.autocommit=autocommit
106+
107+
defcommit(self):
108+
ifself.autocommit==LEGACY_TRANSACTION_CONTROLandnotsqlite3_get_autocommit(self.db):
109+
__exec_stmt(self.db,"COMMIT")
110+
elifself.autocommit==False:
111+
__exec_stmt(self.db,"COMMIT")
112+
__exec_stmt(self.db,"BEGIN")
113+
114+
defrollback(self):
115+
ifself.autocommit==LEGACY_TRANSACTION_CONTROLandnotsqlite3_get_autocommit(self.db):
116+
__exec_stmt(self.db,"ROLLBACK")
117+
elifself.autocommit==False:
118+
__exec_stmt(self.db,"ROLLBACK")
119+
__exec_stmt(self.db,"BEGIN")
77120

78121
defcursor(self):
79-
returnCursor(self.h)
122+
returnCursor(self.db,self.isolation_level,self.autocommit)
80123

81124
defclose(self):
82-
ifself.h:
83-
s=sqlite3_close(self.h)
84-
check_error(self.h,s)
85-
self.h=None
125+
ifself.db:
126+
ifself.autocommit==Falseandnotsqlite3_get_autocommit(self.db):
127+
__exec_stmt(self.db,"ROLLBACK")
128+
129+
res=sqlite3_close(self.db)
130+
check_error(self.db,res)
131+
self.db=None
86132

87133

88134
classCursor:
89-
def__init__(self,h):
90-
self.h=h
91-
self.stmnt=None
135+
def__init__(self,db,isolation_level,autocommit):
136+
self.db=db
137+
self.isolation_level=isolation_level
138+
self.autocommit=autocommit
139+
self.stmt=None
140+
141+
def__quote(val):
142+
ifisinstance(val,str):
143+
return"'%s'"%val
144+
returnstr(val)
92145

93146
defexecute(self,sql,params=None):
94-
ifself.stmnt:
147+
ifself.stmt:
95148
# If there is an existing statement, finalize that to free it
96-
res=sqlite3_finalize(self.stmnt)
97-
check_error(self.h,res)
149+
res=sqlite3_finalize(self.stmt)
150+
check_error(self.db,res)
98151

99152
ifparams:
100-
params= [quote(v)forvinparams]
153+
params= [self.__quote(v)forvinparams]
101154
sql=sql%tuple(params)
102155

103-
stmnt_ptr=bytes(get_ptr_size())
104-
res=sqlite3_prepare(self.h,sql,-1,stmnt_ptr,None)
105-
check_error(self.h,res)
106-
self.stmnt=int.from_bytes(stmnt_ptr,sys.byteorder)
107-
self.num_cols=sqlite3_column_count(self.stmnt)
156+
if__is_dml(sql)andself.autocommit==LEGACY_TRANSACTION_CONTROLandsqlite3_get_autocommit(self.db):
157+
# For compatibility with CPython, add functionality for their default transaction
158+
# behavior. Changing autocommit from LEGACY_TRANSACTION_CONTROL will remove this
159+
__exec_stmt(self.db,"BEGIN "+self.isolation_level)
160+
161+
self.stmt=__prepare_stmt(self.db,sql)
162+
self.num_cols=sqlite3_column_count(self.stmt)
108163

109164
ifnotself.num_cols:
110165
v=self.fetchone()
111166
# If it's not select, actually execute it here
112167
# num_cols == 0 for statements which don't return data (=> modify it)
113168
assertvisNone
114-
self.lastrowid=sqlite3_last_insert_rowid(self.h)
169+
self.lastrowid=sqlite3_last_insert_rowid(self.db)
115170

116171
defclose(self):
117-
ifself.stmnt:
118-
s=sqlite3_finalize(self.stmnt)
119-
check_error(self.h,s)
120-
self.stmnt=None
172+
ifself.stmt:
173+
res=sqlite3_finalize(self.stmt)
174+
check_error(self.db,res)
175+
self.stmt=None
121176

122-
defmake_row(self):
177+
def__make_row(self):
123178
res= []
124179
foriinrange(self.num_cols):
125-
t=sqlite3_column_type(self.stmnt,i)
180+
t=sqlite3_column_type(self.stmt,i)
126181
ift==SQLITE_INTEGER:
127-
res.append(sqlite3_column_int(self.stmnt,i))
182+
res.append(sqlite3_column_int(self.stmt,i))
128183
elift==SQLITE_FLOAT:
129-
res.append(sqlite3_column_double(self.stmnt,i))
184+
res.append(sqlite3_column_double(self.stmt,i))
130185
elift==SQLITE_TEXT:
131-
res.append(sqlite3_column_text(self.stmnt,i))
186+
res.append(sqlite3_column_text(self.stmt,i))
132187
else:
133188
raiseNotImplementedError
134189
returntuple(res)
135190

136191
deffetchone(self):
137-
res=sqlite3_step(self.stmnt)
192+
res=sqlite3_step(self.stmt)
138193
ifres==SQLITE_DONE:
139194
returnNone
140195
ifres==SQLITE_ROW:
141-
returnself.make_row()
142-
check_error(self.h,res)
196+
returnself.__make_row()
197+
check_error(self.db,res)
198+
143199

200+
defconnect(fname,uri=False,isolation_level="",autocommit=LEGACY_TRANSACTION_CONTROL):
201+
ifisolation_levelnotin [None,"","DEFERRED","IMMEDIATE","EXCLUSIVE"]:
202+
raiseError("Invalid option for isolation level")
144203

145-
defconnect(fname,uri=False):
146204
sqlite3_config(SQLITE_CONFIG_URI,int(uri))
147205

148206
sqlite_ptr=bytes(get_ptr_size())
149207
sqlite3_open(fname,sqlite_ptr)
150-
returnConnections(int.from_bytes(sqlite_ptr,sys.byteorder))
208+
db=int.from_bytes(sqlite_ptr,sys.byteorder)
151209

210+
ifautocommit==False:
211+
__exec_stmt(db,"BEGIN")
152212

153-
defquote(val):
154-
ifisinstance(val,str):
155-
return"'%s'"%val
156-
returnstr(val)
213+
returnConnections(db,isolation_level,autocommit)

‎unix-ffi/sqlite3/test_sqlite3_3.py‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
importsqlite3
2+
3+
4+
deftest_autocommit():
5+
conn=sqlite3.connect(":memory:",autocommit=True)
6+
7+
# First cursor creates table and inserts value (DML)
8+
cur=conn.cursor()
9+
cur.execute("CREATE TABLE foo(a int)")
10+
cur.execute("INSERT INTO foo VALUES (42)")
11+
cur.close()
12+
13+
# Second cursor fetches 42 due to the autocommit
14+
cur=conn.cursor()
15+
cur.execute("SELECT * FROM foo")
16+
assertcur.fetchone()== (42,)
17+
assertcur.fetchone()isNone
18+
19+
cur.close()
20+
conn.close()
21+
22+
deftest_manual():
23+
conn=sqlite3.connect(":memory:",autocommit=False)
24+
25+
# First cursor creates table, insert rolls back
26+
cur=conn.cursor()
27+
cur.execute("CREATE TABLE foo(a int)")
28+
conn.commit()
29+
cur.execute("INSERT INTO foo VALUES (42)")
30+
cur.close()
31+
conn.rollback()
32+
33+
# Second connection fetches nothing due to the rollback
34+
cur=conn.cursor()
35+
cur.execute("SELECT * FROM foo")
36+
assertcur.fetchone()isNone
37+
38+
cur.close()
39+
conn.close()
40+
41+
test_autocommit()
42+
test_manual()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp