Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

adodbapitest.py 59KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692
  1. """ Unit tests version 2.6.1.0 for adodbapi"""
  2. """
  3. adodbapi - A python DB API 2.0 interface to Microsoft ADO
  4. Copyright (C) 2002 Henrik Ekelund
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with this library; if not, write to the Free Software
  15. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. Updates by Vernon Cole
  17. """
  18. import copy
  19. import datetime
  20. import decimal
  21. import random
  22. import string
  23. import sys
  24. import unittest
  25. try:
  26. import win32com.client
  27. win32 = True
  28. except ImportError:
  29. win32 = False
  30. # run the configuration module.
  31. import adodbapitestconfig as config # will set sys.path to find correct version of adodbapi
  32. # in our code below, all our switches are from config.whatever
  33. import tryconnection
  34. import adodbapi
  35. import adodbapi.apibase as api
  36. try:
  37. import adodbapi.ado_consts as ado_consts
  38. except ImportError: # we are doing a shortcut import as a module -- so
  39. try:
  40. import ado_consts
  41. except ImportError:
  42. from adodbapi import ado_consts
  43. def str2bytes(sval):
  44. return sval.encode("latin1")
  45. long = int
  46. def randomstring(length):
  47. return "".join([random.choice(string.ascii_letters) for n in range(32)])
  48. class CommonDBTests(unittest.TestCase):
  49. "Self contained super-simple tests in easy syntax, should work on everything between mySQL and Oracle"
  50. def setUp(self):
  51. self.engine = "unknown"
  52. def getEngine(self):
  53. return self.engine
  54. def getConnection(self):
  55. raise NotImplementedError # "This method must be overriden by a subclass"
  56. def getCursor(self):
  57. return self.getConnection().cursor()
  58. def testConnection(self):
  59. crsr = self.getCursor()
  60. assert crsr.__class__.__name__ == "Cursor"
  61. def testErrorHandlerInherits(self):
  62. if not self.remote:
  63. conn = self.getConnection()
  64. mycallable = lambda connection, cursor, errorclass, errorvalue: 1
  65. conn.errorhandler = mycallable
  66. crsr = conn.cursor()
  67. assert (
  68. crsr.errorhandler == mycallable
  69. ), "Error handler on crsr should be same as on connection"
  70. def testDefaultErrorHandlerConnection(self):
  71. if not self.remote:
  72. conn = self.getConnection()
  73. del conn.messages[:]
  74. try:
  75. conn.close()
  76. conn.commit() # Should not be able to use connection after it is closed
  77. except:
  78. assert len(conn.messages) == 1
  79. assert len(conn.messages[0]) == 2
  80. assert conn.messages[0][0] == api.ProgrammingError
  81. def testOwnErrorHandlerConnection(self):
  82. if self.remote: # ToDo: use "skip"
  83. return
  84. mycallable = (
  85. lambda connection, cursor, errorclass, errorvalue: 1
  86. ) # does not raise anything
  87. conn = self.getConnection()
  88. conn.errorhandler = mycallable
  89. conn.close()
  90. conn.commit() # Should not be able to use connection after it is closed
  91. assert len(conn.messages) == 0
  92. conn.errorhandler = None # This should bring back the standard error handler
  93. try:
  94. conn.close()
  95. conn.commit() # Should not be able to use connection after it is closed
  96. except:
  97. pass
  98. # The Standard errorhandler appends error to messages attribute
  99. assert (
  100. len(conn.messages) > 0
  101. ), "Setting errorhandler to none should bring back the standard error handler"
  102. def testDefaultErrorHandlerCursor(self):
  103. crsr = self.getConnection().cursor()
  104. if not self.remote:
  105. del crsr.messages[:]
  106. try:
  107. crsr.execute("SELECT abbtytddrf FROM dasdasd")
  108. except:
  109. assert len(crsr.messages) == 1
  110. assert len(crsr.messages[0]) == 2
  111. assert crsr.messages[0][0] == api.DatabaseError
  112. def testOwnErrorHandlerCursor(self):
  113. if self.remote: # ToDo: should be a "skip"
  114. return
  115. mycallable = (
  116. lambda connection, cursor, errorclass, errorvalue: 1
  117. ) # does not raise anything
  118. crsr = self.getConnection().cursor()
  119. crsr.errorhandler = mycallable
  120. crsr.execute("SELECT abbtytddrf FROM dasdasd")
  121. assert len(crsr.messages) == 0
  122. crsr.errorhandler = None # This should bring back the standard error handler
  123. try:
  124. crsr.execute("SELECT abbtytddrf FROM dasdasd")
  125. except:
  126. pass
  127. # The Standard errorhandler appends error to messages attribute
  128. assert (
  129. len(crsr.messages) > 0
  130. ), "Setting errorhandler to none should bring back the standard error handler"
  131. def testUserDefinedConversions(self):
  132. if self.remote: ## Todo: should be a "skip"
  133. return
  134. try:
  135. duplicatingConverter = lambda aStringField: aStringField * 2
  136. assert duplicatingConverter("gabba") == "gabbagabba"
  137. self.helpForceDropOnTblTemp()
  138. conn = self.getConnection()
  139. # the variantConversions attribute should not exist on a normal connection object
  140. self.assertRaises(AttributeError, lambda x: conn.variantConversions[x], [2])
  141. if not self.remote:
  142. # create a variantConversions attribute on the connection
  143. conn.variantConversions = copy.copy(api.variantConversions)
  144. crsr = conn.cursor()
  145. tabdef = (
  146. "CREATE TABLE xx_%s (fldData VARCHAR(100) NOT NULL, fld2 VARCHAR(20))"
  147. % config.tmp
  148. )
  149. crsr.execute(tabdef)
  150. crsr.execute(
  151. "INSERT INTO xx_%s(fldData,fld2) VALUES('gabba','booga')"
  152. % config.tmp
  153. )
  154. crsr.execute(
  155. "INSERT INTO xx_%s(fldData,fld2) VALUES('hey','yo')" % config.tmp
  156. )
  157. # change converter for ALL adoStringTypes columns
  158. conn.variantConversions[api.adoStringTypes] = duplicatingConverter
  159. crsr.execute(
  160. "SELECT fldData,fld2 FROM xx_%s ORDER BY fldData" % config.tmp
  161. )
  162. rows = crsr.fetchall()
  163. row = rows[0]
  164. self.assertEqual(row[0], "gabbagabba")
  165. row = rows[1]
  166. self.assertEqual(row[0], "heyhey")
  167. self.assertEqual(row[1], "yoyo")
  168. upcaseConverter = lambda aStringField: aStringField.upper()
  169. assert upcaseConverter("upThis") == "UPTHIS"
  170. # now use a single column converter
  171. rows.converters[1] = upcaseConverter # convert second column
  172. self.assertEqual(row[0], "heyhey") # first will be unchanged
  173. self.assertEqual(row[1], "YO") # second will convert to upper case
  174. finally:
  175. try:
  176. del conn.variantConversions # Restore the default
  177. except:
  178. pass
  179. self.helpRollbackTblTemp()
  180. def testUserDefinedConversionForExactNumericTypes(self):
  181. # variantConversions is a dictionary of conversion functions
  182. # held internally in adodbapi.apibase
  183. #
  184. # !!! this test intentionally alters the value of what should be constant in the module
  185. # !!! no new code should use this example, to is only a test to see that the
  186. # !!! deprecated way of doing this still works. (use connection.variantConversions)
  187. #
  188. if not self.remote and sys.version_info < (3, 0): ### Py3 need different test
  189. oldconverter = adodbapi.variantConversions[
  190. ado_consts.adNumeric
  191. ] # keep old function to restore later
  192. # By default decimal and "numbers" are returned as decimals.
  193. # Instead, make numbers return as floats
  194. try:
  195. adodbapi.variantConversions[ado_consts.adNumeric] = adodbapi.cvtFloat
  196. self.helpTestDataType(
  197. "decimal(18,2)", "NUMBER", 3.45, compareAlmostEqual=1
  198. )
  199. self.helpTestDataType(
  200. "numeric(18,2)", "NUMBER", 3.45, compareAlmostEqual=1
  201. )
  202. # now return strings
  203. adodbapi.variantConversions[ado_consts.adNumeric] = adodbapi.cvtString
  204. self.helpTestDataType("numeric(18,2)", "NUMBER", "3.45")
  205. # now a completly weird user defined convertion
  206. adodbapi.variantConversions[ado_consts.adNumeric] = (
  207. lambda x: "!!This function returns a funny unicode string %s!!" % x
  208. )
  209. self.helpTestDataType(
  210. "numeric(18,2)",
  211. "NUMBER",
  212. "3.45",
  213. allowedReturnValues=[
  214. "!!This function returns a funny unicode string 3.45!!"
  215. ],
  216. )
  217. finally:
  218. # now reset the converter to its original function
  219. adodbapi.variantConversions[
  220. ado_consts.adNumeric
  221. ] = oldconverter # Restore the original convertion function
  222. def helpTestDataType(
  223. self,
  224. sqlDataTypeString,
  225. DBAPIDataTypeString,
  226. pyData,
  227. pyDataInputAlternatives=None,
  228. compareAlmostEqual=None,
  229. allowedReturnValues=None,
  230. ):
  231. self.helpForceDropOnTblTemp()
  232. conn = self.getConnection()
  233. crsr = conn.cursor()
  234. tabdef = (
  235. """
  236. CREATE TABLE xx_%s (
  237. fldId integer NOT NULL,
  238. fldData """
  239. % config.tmp
  240. + sqlDataTypeString
  241. + ")\n"
  242. )
  243. crsr.execute(tabdef)
  244. # Test Null values mapped to None
  245. crsr.execute("INSERT INTO xx_%s (fldId) VALUES (1)" % config.tmp)
  246. crsr.execute("SELECT fldId,fldData FROM xx_%s" % config.tmp)
  247. rs = crsr.fetchone()
  248. self.assertEqual(rs[1], None) # Null should be mapped to None
  249. assert rs[0] == 1
  250. # Test description related
  251. descTuple = crsr.description[1]
  252. assert descTuple[0] in ["fldData", "flddata"], 'was "%s" expected "%s"' % (
  253. descTuple[0],
  254. "fldData",
  255. )
  256. if DBAPIDataTypeString == "STRING":
  257. assert descTuple[1] == api.STRING, 'was "%s" expected "%s"' % (
  258. descTuple[1],
  259. api.STRING.values,
  260. )
  261. elif DBAPIDataTypeString == "NUMBER":
  262. assert descTuple[1] == api.NUMBER, 'was "%s" expected "%s"' % (
  263. descTuple[1],
  264. api.NUMBER.values,
  265. )
  266. elif DBAPIDataTypeString == "BINARY":
  267. assert descTuple[1] == api.BINARY, 'was "%s" expected "%s"' % (
  268. descTuple[1],
  269. api.BINARY.values,
  270. )
  271. elif DBAPIDataTypeString == "DATETIME":
  272. assert descTuple[1] == api.DATETIME, 'was "%s" expected "%s"' % (
  273. descTuple[1],
  274. api.DATETIME.values,
  275. )
  276. elif DBAPIDataTypeString == "ROWID":
  277. assert descTuple[1] == api.ROWID, 'was "%s" expected "%s"' % (
  278. descTuple[1],
  279. api.ROWID.values,
  280. )
  281. elif DBAPIDataTypeString == "UUID":
  282. assert descTuple[1] == api.OTHER, 'was "%s" expected "%s"' % (
  283. descTuple[1],
  284. api.OTHER.values,
  285. )
  286. else:
  287. raise NotImplementedError # "DBAPIDataTypeString not provided"
  288. # Test data binding
  289. inputs = [pyData]
  290. if pyDataInputAlternatives:
  291. inputs.extend(pyDataInputAlternatives)
  292. inputs = set(inputs) # removes redundant string==unicode tests
  293. fldId = 1
  294. for inParam in inputs:
  295. fldId += 1
  296. try:
  297. crsr.execute(
  298. "INSERT INTO xx_%s (fldId,fldData) VALUES (?,?)" % config.tmp,
  299. (fldId, inParam),
  300. )
  301. except:
  302. if self.remote:
  303. for message in crsr.messages:
  304. print(message)
  305. else:
  306. conn.printADOerrors()
  307. raise
  308. crsr.execute(
  309. "SELECT fldData FROM xx_%s WHERE ?=fldID" % config.tmp, [fldId]
  310. )
  311. rs = crsr.fetchone()
  312. if allowedReturnValues:
  313. allowedTypes = tuple([type(aRV) for aRV in allowedReturnValues])
  314. assert isinstance(
  315. rs[0], allowedTypes
  316. ), 'result type "%s" must be one of %s' % (type(rs[0]), allowedTypes)
  317. else:
  318. assert isinstance(
  319. rs[0], type(pyData)
  320. ), 'result type "%s" must be instance of %s' % (
  321. type(rs[0]),
  322. type(pyData),
  323. )
  324. if compareAlmostEqual and DBAPIDataTypeString == "DATETIME":
  325. iso1 = adodbapi.dateconverter.DateObjectToIsoFormatString(rs[0])
  326. iso2 = adodbapi.dateconverter.DateObjectToIsoFormatString(pyData)
  327. self.assertEqual(iso1, iso2)
  328. elif compareAlmostEqual:
  329. s = float(pyData)
  330. v = float(rs[0])
  331. assert (
  332. abs(v - s) / s < 0.00001
  333. ), "Values not almost equal recvd=%s, expected=%f" % (rs[0], s)
  334. else:
  335. if allowedReturnValues:
  336. ok = False
  337. self.assertTrue(
  338. rs[0] in allowedReturnValues,
  339. 'Value "%s" not in %s' % (repr(rs[0]), allowedReturnValues),
  340. )
  341. else:
  342. self.assertEqual(
  343. rs[0],
  344. pyData,
  345. 'Values are not equal recvd="%s", expected="%s"'
  346. % (rs[0], pyData),
  347. )
  348. def testDataTypeFloat(self):
  349. self.helpTestDataType("real", "NUMBER", 3.45, compareAlmostEqual=True)
  350. self.helpTestDataType("float", "NUMBER", 1.79e37, compareAlmostEqual=True)
  351. def testDataTypeDecmal(self):
  352. self.helpTestDataType(
  353. "decimal(18,2)",
  354. "NUMBER",
  355. 3.45,
  356. allowedReturnValues=["3.45", "3,45", decimal.Decimal("3.45")],
  357. )
  358. self.helpTestDataType(
  359. "numeric(18,2)",
  360. "NUMBER",
  361. 3.45,
  362. allowedReturnValues=["3.45", "3,45", decimal.Decimal("3.45")],
  363. )
  364. self.helpTestDataType(
  365. "decimal(20,2)",
  366. "NUMBER",
  367. 444444444444444444,
  368. allowedReturnValues=[
  369. "444444444444444444.00",
  370. "444444444444444444,00",
  371. decimal.Decimal("444444444444444444"),
  372. ],
  373. )
  374. if self.getEngine() == "MSSQL":
  375. self.helpTestDataType(
  376. "uniqueidentifier",
  377. "UUID",
  378. "{71A4F49E-39F3-42B1-A41E-48FF154996E6}",
  379. allowedReturnValues=["{71A4F49E-39F3-42B1-A41E-48FF154996E6}"],
  380. )
  381. def testDataTypeMoney(self): # v2.1 Cole -- use decimal for money
  382. if self.getEngine() == "MySQL":
  383. self.helpTestDataType(
  384. "DECIMAL(20,4)", "NUMBER", decimal.Decimal("-922337203685477.5808")
  385. )
  386. elif self.getEngine() == "PostgreSQL":
  387. self.helpTestDataType(
  388. "money",
  389. "NUMBER",
  390. decimal.Decimal("-922337203685477.5808"),
  391. compareAlmostEqual=True,
  392. allowedReturnValues=[
  393. -922337203685477.5808,
  394. decimal.Decimal("-922337203685477.5808"),
  395. ],
  396. )
  397. else:
  398. self.helpTestDataType("smallmoney", "NUMBER", decimal.Decimal("214748.02"))
  399. self.helpTestDataType(
  400. "money", "NUMBER", decimal.Decimal("-922337203685477.5808")
  401. )
  402. def testDataTypeInt(self):
  403. if self.getEngine() != "PostgreSQL":
  404. self.helpTestDataType("tinyint", "NUMBER", 115)
  405. self.helpTestDataType("smallint", "NUMBER", -32768)
  406. if self.getEngine() not in ["ACCESS", "PostgreSQL"]:
  407. self.helpTestDataType(
  408. "bit", "NUMBER", 1
  409. ) # Does not work correctly with access
  410. if self.getEngine() in ["MSSQL", "PostgreSQL"]:
  411. self.helpTestDataType(
  412. "bigint",
  413. "NUMBER",
  414. 3000000000,
  415. allowedReturnValues=[3000000000, int(3000000000)],
  416. )
  417. self.helpTestDataType("int", "NUMBER", 2147483647)
  418. def testDataTypeChar(self):
  419. for sqlDataType in ("char(6)", "nchar(6)"):
  420. self.helpTestDataType(
  421. sqlDataType,
  422. "STRING",
  423. "spam ",
  424. allowedReturnValues=["spam", "spam", "spam ", "spam "],
  425. )
  426. def testDataTypeVarChar(self):
  427. if self.getEngine() == "MySQL":
  428. stringKinds = ["varchar(10)", "text"]
  429. elif self.getEngine() == "PostgreSQL":
  430. stringKinds = ["varchar(10)", "text", "character varying"]
  431. else:
  432. stringKinds = [
  433. "varchar(10)",
  434. "nvarchar(10)",
  435. "text",
  436. "ntext",
  437. ] # ,"varchar(max)"]
  438. for sqlDataType in stringKinds:
  439. self.helpTestDataType(sqlDataType, "STRING", "spam", ["spam"])
  440. def testDataTypeDate(self):
  441. if self.getEngine() == "PostgreSQL":
  442. dt = "timestamp"
  443. else:
  444. dt = "datetime"
  445. self.helpTestDataType(
  446. dt, "DATETIME", adodbapi.Date(2002, 10, 28), compareAlmostEqual=True
  447. )
  448. if self.getEngine() not in ["MySQL", "PostgreSQL"]:
  449. self.helpTestDataType(
  450. "smalldatetime",
  451. "DATETIME",
  452. adodbapi.Date(2002, 10, 28),
  453. compareAlmostEqual=True,
  454. )
  455. if tag != "pythontime" and self.getEngine() not in [
  456. "MySQL",
  457. "PostgreSQL",
  458. ]: # fails when using pythonTime
  459. self.helpTestDataType(
  460. dt,
  461. "DATETIME",
  462. adodbapi.Timestamp(2002, 10, 28, 12, 15, 1),
  463. compareAlmostEqual=True,
  464. )
  465. def testDataTypeBinary(self):
  466. binfld = str2bytes("\x07\x00\xE2\x40*")
  467. arv = [binfld, adodbapi.Binary(binfld), bytes(binfld)]
  468. if self.getEngine() == "PostgreSQL":
  469. self.helpTestDataType(
  470. "bytea", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv
  471. )
  472. else:
  473. self.helpTestDataType(
  474. "binary(5)", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv
  475. )
  476. self.helpTestDataType(
  477. "varbinary(100)",
  478. "BINARY",
  479. adodbapi.Binary(binfld),
  480. allowedReturnValues=arv,
  481. )
  482. if self.getEngine() != "MySQL":
  483. self.helpTestDataType(
  484. "image", "BINARY", adodbapi.Binary(binfld), allowedReturnValues=arv
  485. )
  486. def helpRollbackTblTemp(self):
  487. self.helpForceDropOnTblTemp()
  488. def helpForceDropOnTblTemp(self):
  489. conn = self.getConnection()
  490. with conn.cursor() as crsr:
  491. try:
  492. crsr.execute("DROP TABLE xx_%s" % config.tmp)
  493. if not conn.autocommit:
  494. conn.commit()
  495. except:
  496. pass
  497. def helpCreateAndPopulateTableTemp(self, crsr):
  498. tabdef = (
  499. """
  500. CREATE TABLE xx_%s (
  501. fldData INTEGER
  502. )
  503. """
  504. % config.tmp
  505. )
  506. try: # EAFP
  507. crsr.execute(tabdef)
  508. except api.DatabaseError: # was not dropped before
  509. self.helpForceDropOnTblTemp() # so drop it now
  510. crsr.execute(tabdef)
  511. for i in range(9): # note: this poor SQL code, but a valid test
  512. crsr.execute("INSERT INTO xx_%s (fldData) VALUES (%i)" % (config.tmp, i))
  513. # NOTE: building the test table without using parameter substitution
  514. def testFetchAll(self):
  515. crsr = self.getCursor()
  516. self.helpCreateAndPopulateTableTemp(crsr)
  517. crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
  518. rs = crsr.fetchall()
  519. assert len(rs) == 9
  520. # test slice of rows
  521. i = 3
  522. for row in rs[3:-2]: # should have rowid 3..6
  523. assert row[0] == i
  524. i += 1
  525. self.helpRollbackTblTemp()
  526. def testPreparedStatement(self):
  527. crsr = self.getCursor()
  528. self.helpCreateAndPopulateTableTemp(crsr)
  529. crsr.prepare("SELECT fldData FROM xx_%s" % config.tmp)
  530. crsr.execute(crsr.command) # remember the one that was prepared
  531. rs = crsr.fetchall()
  532. assert len(rs) == 9
  533. assert rs[2][0] == 2
  534. self.helpRollbackTblTemp()
  535. def testWrongPreparedStatement(self):
  536. crsr = self.getCursor()
  537. self.helpCreateAndPopulateTableTemp(crsr)
  538. crsr.prepare("SELECT * FROM nowhere")
  539. crsr.execute(
  540. "SELECT fldData FROM xx_%s" % config.tmp
  541. ) # should execute this one, not the prepared one
  542. rs = crsr.fetchall()
  543. assert len(rs) == 9
  544. assert rs[2][0] == 2
  545. self.helpRollbackTblTemp()
  546. def testIterator(self):
  547. crsr = self.getCursor()
  548. self.helpCreateAndPopulateTableTemp(crsr)
  549. crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
  550. for i, row in enumerate(
  551. crsr
  552. ): # using cursor as an iterator, rather than fetchxxx
  553. assert row[0] == i
  554. self.helpRollbackTblTemp()
  555. def testExecuteMany(self):
  556. crsr = self.getCursor()
  557. self.helpCreateAndPopulateTableTemp(crsr)
  558. seq_of_values = [(111,), (222,)]
  559. crsr.executemany(
  560. "INSERT INTO xx_%s (fldData) VALUES (?)" % config.tmp, seq_of_values
  561. )
  562. if crsr.rowcount == -1:
  563. print(
  564. self.getEngine()
  565. + " Provider does not support rowcount (on .executemany())"
  566. )
  567. else:
  568. self.assertEqual(crsr.rowcount, 2)
  569. crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
  570. rs = crsr.fetchall()
  571. assert len(rs) == 11
  572. self.helpRollbackTblTemp()
  573. def testRowCount(self):
  574. crsr = self.getCursor()
  575. self.helpCreateAndPopulateTableTemp(crsr)
  576. crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
  577. if crsr.rowcount == -1:
  578. # print("provider does not support rowcount on select")
  579. pass
  580. else:
  581. self.assertEqual(crsr.rowcount, 9)
  582. self.helpRollbackTblTemp()
  583. def testRowCountNoRecordset(self):
  584. crsr = self.getCursor()
  585. self.helpCreateAndPopulateTableTemp(crsr)
  586. crsr.execute("DELETE FROM xx_%s WHERE fldData >= 5" % config.tmp)
  587. if crsr.rowcount == -1:
  588. print(self.getEngine() + " Provider does not support rowcount (on DELETE)")
  589. else:
  590. self.assertEqual(crsr.rowcount, 4)
  591. self.helpRollbackTblTemp()
  592. def testFetchMany(self):
  593. crsr = self.getCursor()
  594. self.helpCreateAndPopulateTableTemp(crsr)
  595. crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
  596. rs = crsr.fetchmany(3)
  597. assert len(rs) == 3
  598. rs = crsr.fetchmany(5)
  599. assert len(rs) == 5
  600. rs = crsr.fetchmany(5)
  601. assert len(rs) == 1 # Asked for five, but there is only one left
  602. self.helpRollbackTblTemp()
  603. def testFetchManyWithArraySize(self):
  604. crsr = self.getCursor()
  605. self.helpCreateAndPopulateTableTemp(crsr)
  606. crsr.execute("SELECT fldData FROM xx_%s" % config.tmp)
  607. rs = crsr.fetchmany()
  608. assert len(rs) == 1 # arraysize Defaults to one
  609. crsr.arraysize = 4
  610. rs = crsr.fetchmany()
  611. assert len(rs) == 4
  612. rs = crsr.fetchmany()
  613. assert len(rs) == 4
  614. rs = crsr.fetchmany()
  615. assert len(rs) == 0
  616. self.helpRollbackTblTemp()
  617. def testErrorConnect(self):
  618. conn = self.getConnection()
  619. kw = {}
  620. if "proxy_host" in conn.kwargs:
  621. kw["proxy_host"] = conn.kwargs["proxy_host"]
  622. conn.close()
  623. self.assertRaises(api.DatabaseError, self.db, "not a valid connect string", kw)
  624. def testRowIterator(self):
  625. self.helpForceDropOnTblTemp()
  626. conn = self.getConnection()
  627. crsr = conn.cursor()
  628. tabdef = (
  629. """
  630. CREATE TABLE xx_%s (
  631. fldId integer NOT NULL,
  632. fldTwo integer,
  633. fldThree integer,
  634. fldFour integer)
  635. """
  636. % config.tmp
  637. )
  638. crsr.execute(tabdef)
  639. inputs = [(2, 3, 4), (102, 103, 104)]
  640. fldId = 1
  641. for inParam in inputs:
  642. fldId += 1
  643. try:
  644. crsr.execute(
  645. "INSERT INTO xx_%s (fldId,fldTwo,fldThree,fldFour) VALUES (?,?,?,?)"
  646. % config.tmp,
  647. (fldId, inParam[0], inParam[1], inParam[2]),
  648. )
  649. except:
  650. if self.remote:
  651. for message in crsr.messages:
  652. print(message)
  653. else:
  654. conn.printADOerrors()
  655. raise
  656. crsr.execute(
  657. "SELECT fldTwo,fldThree,fldFour FROM xx_%s WHERE ?=fldID" % config.tmp,
  658. [fldId],
  659. )
  660. rec = crsr.fetchone()
  661. # check that stepping through an emulated row works
  662. for j in range(len(inParam)):
  663. assert (
  664. rec[j] == inParam[j]
  665. ), 'returned value:"%s" != test value:"%s"' % (rec[j], inParam[j])
  666. # check that we can get a complete tuple from a row
  667. assert tuple(rec) == inParam, 'returned value:"%s" != test value:"%s"' % (
  668. repr(rec),
  669. repr(inParam),
  670. )
  671. # test that slices of rows work
  672. slice1 = tuple(rec[:-1])
  673. slice2 = tuple(inParam[0:2])
  674. assert slice1 == slice2, 'returned value:"%s" != test value:"%s"' % (
  675. repr(slice1),
  676. repr(slice2),
  677. )
  678. # now test named column retrieval
  679. assert rec["fldTwo"] == inParam[0]
  680. assert rec.fldThree == inParam[1]
  681. assert rec.fldFour == inParam[2]
  682. # test array operation
  683. # note that the fields vv vv vv are out of order
  684. crsr.execute("select fldThree,fldFour,fldTwo from xx_%s" % config.tmp)
  685. recs = crsr.fetchall()
  686. assert recs[1][0] == 103
  687. assert recs[0][1] == 4
  688. assert recs[1]["fldFour"] == 104
  689. assert recs[0, 0] == 3
  690. assert recs[0, "fldTwo"] == 2
  691. assert recs[1, 2] == 102
  692. for i in range(1):
  693. for j in range(2):
  694. assert recs[i][j] == recs[i, j]
  695. def testFormatParamstyle(self):
  696. self.helpForceDropOnTblTemp()
  697. conn = self.getConnection()
  698. conn.paramstyle = "format" # test nonstandard use of paramstyle
  699. crsr = conn.cursor()
  700. tabdef = (
  701. """
  702. CREATE TABLE xx_%s (
  703. fldId integer NOT NULL,
  704. fldData varchar(10),
  705. fldConst varchar(30))
  706. """
  707. % config.tmp
  708. )
  709. crsr.execute(tabdef)
  710. inputs = ["one", "two", "three"]
  711. fldId = 2
  712. for inParam in inputs:
  713. fldId += 1
  714. sql = (
  715. "INSERT INTO xx_"
  716. + config.tmp
  717. + " (fldId,fldConst,fldData) VALUES (%s,'thi%s :may cause? trouble', %s)"
  718. )
  719. try:
  720. crsr.execute(sql, (fldId, inParam))
  721. except:
  722. if self.remote:
  723. for message in crsr.messages:
  724. print(message)
  725. else:
  726. conn.printADOerrors()
  727. raise
  728. crsr.execute(
  729. "SELECT fldData, fldConst FROM xx_" + config.tmp + " WHERE %s=fldID",
  730. [fldId],
  731. )
  732. rec = crsr.fetchone()
  733. self.assertEqual(
  734. rec[0],
  735. inParam,
  736. 'returned value:"%s" != test value:"%s"' % (rec[0], inParam),
  737. )
  738. self.assertEqual(rec[1], "thi%s :may cause? trouble")
  739. # now try an operation with a "%s" as part of a literal
  740. sel = (
  741. "insert into xx_" + config.tmp + " (fldId,fldData) VALUES (%s,'four%sfive')"
  742. )
  743. params = (20,)
  744. crsr.execute(sel, params)
  745. # test the .query implementation
  746. assert "(?," in crsr.query, 'expected:"%s" in "%s"' % ("(?,", crsr.query)
  747. # test the .command attribute
  748. assert crsr.command == sel, 'expected:"%s" but found "%s"' % (sel, crsr.command)
  749. # test the .parameters attribute
  750. if not self.remote: # parameter list will be altered in transit
  751. self.assertEqual(crsr.parameters, params)
  752. # now make sure the data made it
  753. crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=20" % config.tmp)
  754. rec = crsr.fetchone()
  755. self.assertEqual(rec[0], "four%sfive")
  756. def testNamedParamstyle(self):
  757. self.helpForceDropOnTblTemp()
  758. conn = self.getConnection()
  759. crsr = conn.cursor()
  760. crsr.paramstyle = "named" # test nonstandard use of paramstyle
  761. tabdef = (
  762. """
  763. CREATE TABLE xx_%s (
  764. fldId integer NOT NULL,
  765. fldData varchar(10))
  766. """
  767. % config.tmp
  768. )
  769. crsr.execute(tabdef)
  770. inputs = ["four", "five", "six"]
  771. fldId = 10
  772. for inParam in inputs:
  773. fldId += 1
  774. try:
  775. crsr.execute(
  776. "INSERT INTO xx_%s (fldId,fldData) VALUES (:Id,:f_Val)"
  777. % config.tmp,
  778. {"f_Val": inParam, "Id": fldId},
  779. )
  780. except:
  781. if self.remote:
  782. for message in crsr.messages:
  783. print(message)
  784. else:
  785. conn.printADOerrors()
  786. raise
  787. crsr.execute(
  788. "SELECT fldData FROM xx_%s WHERE fldID=:Id" % config.tmp, {"Id": fldId}
  789. )
  790. rec = crsr.fetchone()
  791. self.assertEqual(
  792. rec[0],
  793. inParam,
  794. 'returned value:"%s" != test value:"%s"' % (rec[0], inParam),
  795. )
  796. # now a test with a ":" as part of a literal
  797. crsr.execute(
  798. "insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp,
  799. {"xyz": 30},
  800. )
  801. crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp)
  802. rec = crsr.fetchone()
  803. self.assertEqual(rec[0], "six:five")
  804. def testPyformatParamstyle(self):
  805. self.helpForceDropOnTblTemp()
  806. conn = self.getConnection()
  807. crsr = conn.cursor()
  808. crsr.paramstyle = "pyformat" # test nonstandard use of paramstyle
  809. tabdef = (
  810. """
  811. CREATE TABLE xx_%s (
  812. fldId integer NOT NULL,
  813. fldData varchar(10))
  814. """
  815. % config.tmp
  816. )
  817. crsr.execute(tabdef)
  818. inputs = ["four", "five", "six"]
  819. fldId = 10
  820. for inParam in inputs:
  821. fldId += 1
  822. try:
  823. crsr.execute(
  824. "INSERT INTO xx_%s (fldId,fldData) VALUES (%%(Id)s,%%(f_Val)s)"
  825. % config.tmp,
  826. {"f_Val": inParam, "Id": fldId},
  827. )
  828. except:
  829. if self.remote:
  830. for message in crsr.messages:
  831. print(message)
  832. else:
  833. conn.printADOerrors()
  834. raise
  835. crsr.execute(
  836. "SELECT fldData FROM xx_%s WHERE fldID=%%(Id)s" % config.tmp,
  837. {"Id": fldId},
  838. )
  839. rec = crsr.fetchone()
  840. self.assertEqual(
  841. rec[0],
  842. inParam,
  843. 'returned value:"%s" != test value:"%s"' % (rec[0], inParam),
  844. )
  845. # now a test with a "%" as part of a literal
  846. crsr.execute(
  847. "insert into xx_%s (fldId,fldData) VALUES (%%(xyz)s,'six%%five')"
  848. % config.tmp,
  849. {"xyz": 30},
  850. )
  851. crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp)
  852. rec = crsr.fetchone()
  853. self.assertEqual(rec[0], "six%five")
  854. def testAutomaticParamstyle(self):
  855. self.helpForceDropOnTblTemp()
  856. conn = self.getConnection()
  857. conn.paramstyle = "dynamic" # test nonstandard use of paramstyle
  858. crsr = conn.cursor()
  859. tabdef = (
  860. """
  861. CREATE TABLE xx_%s (
  862. fldId integer NOT NULL,
  863. fldData varchar(10),
  864. fldConst varchar(30))
  865. """
  866. % config.tmp
  867. )
  868. crsr.execute(tabdef)
  869. inputs = ["one", "two", "three"]
  870. fldId = 2
  871. for inParam in inputs:
  872. fldId += 1
  873. try:
  874. crsr.execute(
  875. "INSERT INTO xx_"
  876. + config.tmp
  877. + " (fldId,fldConst,fldData) VALUES (?,'thi%s :may cause? troub:1e', ?)",
  878. (fldId, inParam),
  879. )
  880. except:
  881. if self.remote:
  882. for message in crsr.messages:
  883. print(message)
  884. else:
  885. conn.printADOerrors()
  886. raise
  887. trouble = "thi%s :may cause? troub:1e"
  888. crsr.execute(
  889. "SELECT fldData, fldConst FROM xx_" + config.tmp + " WHERE ?=fldID",
  890. [fldId],
  891. )
  892. rec = crsr.fetchone()
  893. self.assertEqual(
  894. rec[0],
  895. inParam,
  896. 'returned value:"%s" != test value:"%s"' % (rec[0], inParam),
  897. )
  898. self.assertEqual(rec[1], trouble)
  899. # inputs = [u'four',u'five',u'six']
  900. fldId = 10
  901. for inParam in inputs:
  902. fldId += 1
  903. try:
  904. crsr.execute(
  905. "INSERT INTO xx_%s (fldId,fldData) VALUES (:Id,:f_Val)"
  906. % config.tmp,
  907. {"f_Val": inParam, "Id": fldId},
  908. )
  909. except:
  910. if self.remote:
  911. for message in crsr.messages:
  912. print(message)
  913. else:
  914. conn.printADOerrors()
  915. raise
  916. crsr.execute(
  917. "SELECT fldData FROM xx_%s WHERE :Id=fldID" % config.tmp, {"Id": fldId}
  918. )
  919. rec = crsr.fetchone()
  920. self.assertEqual(
  921. rec[0],
  922. inParam,
  923. 'returned value:"%s" != test value:"%s"' % (rec[0], inParam),
  924. )
  925. # now a test with a ":" as part of a literal -- and use a prepared query
  926. ppdcmd = (
  927. "insert into xx_%s (fldId,fldData) VALUES (:xyz,'six:five')" % config.tmp
  928. )
  929. crsr.prepare(ppdcmd)
  930. crsr.execute(ppdcmd, {"xyz": 30})
  931. crsr.execute("SELECT fldData FROM xx_%s WHERE fldID=30" % config.tmp)
  932. rec = crsr.fetchone()
  933. self.assertEqual(rec[0], "six:five")
  934. def testRollBack(self):
  935. conn = self.getConnection()
  936. crsr = conn.cursor()
  937. assert not crsr.connection.autocommit, "Unexpected beginning condition"
  938. self.helpCreateAndPopulateTableTemp(crsr)
  939. crsr.connection.commit() # commit the first bunch
  940. crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
  941. selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp
  942. crsr.execute(selectSql)
  943. rs = crsr.fetchall()
  944. assert len(rs) == 1
  945. self.conn.rollback()
  946. crsr.execute(selectSql)
  947. assert (
  948. crsr.fetchone() == None
  949. ), "cursor.fetchone should return None if a query retrieves no rows"
  950. crsr.execute("SELECT fldData from xx_%s" % config.tmp)
  951. rs = crsr.fetchall()
  952. assert len(rs) == 9, "the original records should still be present"
  953. self.helpRollbackTblTemp()
  954. def testCommit(self):
  955. try:
  956. con2 = self.getAnotherConnection()
  957. except NotImplementedError:
  958. return # should be "SKIP" for ACCESS
  959. assert not con2.autocommit, "default should be manual commit"
  960. crsr = con2.cursor()
  961. self.helpCreateAndPopulateTableTemp(crsr)
  962. crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
  963. con2.commit()
  964. selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp
  965. crsr.execute(selectSql)
  966. rs = crsr.fetchall()
  967. assert len(rs) == 1
  968. crsr.close()
  969. con2.close()
  970. conn = self.getConnection()
  971. crsr = self.getCursor()
  972. with conn.cursor() as crsr:
  973. crsr.execute(selectSql)
  974. rs = crsr.fetchall()
  975. assert len(rs) == 1
  976. assert rs[0][0] == 100
  977. self.helpRollbackTblTemp()
  978. def testAutoRollback(self):
  979. try:
  980. con2 = self.getAnotherConnection()
  981. except NotImplementedError:
  982. return # should be "SKIP" for ACCESS
  983. assert not con2.autocommit, "unexpected beginning condition"
  984. crsr = con2.cursor()
  985. self.helpCreateAndPopulateTableTemp(crsr)
  986. crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
  987. selectSql = "SELECT fldData FROM xx_%s WHERE fldData=100" % config.tmp
  988. crsr.execute(selectSql)
  989. rs = crsr.fetchall()
  990. assert len(rs) == 1
  991. crsr.close()
  992. con2.close()
  993. crsr = self.getCursor()
  994. try:
  995. crsr.execute(
  996. selectSql
  997. ) # closing the connection should have forced rollback
  998. row = crsr.fetchone()
  999. except api.DatabaseError:
  1000. row = None # if the entire table disappeared the rollback was perfect and the test passed
  1001. assert row == None, (
  1002. "cursor.fetchone should return None if a query retrieves no rows. Got %s"
  1003. % repr(row)
  1004. )
  1005. self.helpRollbackTblTemp()
  1006. def testAutoCommit(self):
  1007. try:
  1008. ac_conn = self.getAnotherConnection({"autocommit": True})
  1009. except NotImplementedError:
  1010. return # should be "SKIP" for ACCESS
  1011. crsr = ac_conn.cursor()
  1012. self.helpCreateAndPopulateTableTemp(crsr)
  1013. crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
  1014. crsr.close()
  1015. with self.getCursor() as crsr:
  1016. selectSql = "SELECT fldData from xx_%s" % config.tmp
  1017. crsr.execute(
  1018. selectSql
  1019. ) # closing the connection should _not_ have forced rollback
  1020. rs = crsr.fetchall()
  1021. assert len(rs) == 10, "all records should still be present"
  1022. ac_conn.close()
  1023. self.helpRollbackTblTemp()
  1024. def testSwitchedAutoCommit(self):
  1025. try:
  1026. ac_conn = self.getAnotherConnection()
  1027. except NotImplementedError:
  1028. return # should be "SKIP" for ACCESS
  1029. ac_conn.autocommit = True
  1030. crsr = ac_conn.cursor()
  1031. self.helpCreateAndPopulateTableTemp(crsr)
  1032. crsr.execute("INSERT INTO xx_%s (fldData) VALUES(100)" % config.tmp)
  1033. crsr.close()
  1034. conn = self.getConnection()
  1035. ac_conn.close()
  1036. with self.getCursor() as crsr:
  1037. selectSql = "SELECT fldData from xx_%s" % config.tmp
  1038. crsr.execute(
  1039. selectSql
  1040. ) # closing the connection should _not_ have forced rollback
  1041. rs = crsr.fetchall()
  1042. assert len(rs) == 10, "all records should still be present"
  1043. self.helpRollbackTblTemp()
  1044. def testExtendedTypeHandling(self):
  1045. class XtendString(str):
  1046. pass
  1047. class XtendInt(int):
  1048. pass
  1049. class XtendFloat(float):
  1050. pass
  1051. xs = XtendString(randomstring(30))
  1052. xi = XtendInt(random.randint(-100, 500))
  1053. xf = XtendFloat(random.random())
  1054. self.helpForceDropOnTblTemp()
  1055. conn = self.getConnection()
  1056. crsr = conn.cursor()
  1057. tabdef = (
  1058. """
  1059. CREATE TABLE xx_%s (
  1060. s VARCHAR(40) NOT NULL,
  1061. i INTEGER NOT NULL,
  1062. f REAL NOT NULL)"""
  1063. % config.tmp
  1064. )
  1065. crsr.execute(tabdef)
  1066. crsr.execute(
  1067. "INSERT INTO xx_%s (s, i, f) VALUES (?, ?, ?)" % config.tmp, (xs, xi, xf)
  1068. )
  1069. crsr.close()
  1070. conn = self.getConnection()
  1071. with self.getCursor() as crsr:
  1072. selectSql = "SELECT s, i, f from xx_%s" % config.tmp
  1073. crsr.execute(
  1074. selectSql
  1075. ) # closing the connection should _not_ have forced rollback
  1076. row = crsr.fetchone()
  1077. self.assertEqual(row.s, xs)
  1078. self.assertEqual(row.i, xi)
  1079. self.assertAlmostEqual(row.f, xf)
  1080. self.helpRollbackTblTemp()
  1081. class TestADOwithSQLServer(CommonDBTests):
  1082. def setUp(self):
  1083. self.conn = config.dbSqlServerconnect(
  1084. *config.connStrSQLServer[0], **config.connStrSQLServer[1]
  1085. )
  1086. self.conn.timeout = 30 # turn timeout back up
  1087. self.engine = "MSSQL"
  1088. self.db = config.dbSqlServerconnect
  1089. self.remote = config.connStrSQLServer[2]
  1090. def tearDown(self):
  1091. try:
  1092. self.conn.rollback()
  1093. except:
  1094. pass
  1095. try:
  1096. self.conn.close()
  1097. except:
  1098. pass
  1099. self.conn = None
  1100. def getConnection(self):
  1101. return self.conn
  1102. def getAnotherConnection(self, addkeys=None):
  1103. keys = dict(config.connStrSQLServer[1])
  1104. if addkeys:
  1105. keys.update(addkeys)
  1106. return config.dbSqlServerconnect(*config.connStrSQLServer[0], **keys)
  1107. def testVariableReturningStoredProcedure(self):
  1108. crsr = self.conn.cursor()
  1109. spdef = """
  1110. CREATE PROCEDURE sp_DeleteMeOnlyForTesting
  1111. @theInput varchar(50),
  1112. @theOtherInput varchar(50),
  1113. @theOutput varchar(100) OUTPUT
  1114. AS
  1115. SET @theOutput=@theInput+@theOtherInput
  1116. """
  1117. try:
  1118. crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting")
  1119. self.conn.commit()
  1120. except: # Make sure it is empty
  1121. pass
  1122. crsr.execute(spdef)
  1123. retvalues = crsr.callproc(
  1124. "sp_DeleteMeOnlyForTesting", ("Dodsworth", "Anne", " ")
  1125. )
  1126. assert retvalues[0] == "Dodsworth", '%s is not "Dodsworth"' % repr(retvalues[0])
  1127. assert retvalues[1] == "Anne", '%s is not "Anne"' % repr(retvalues[1])
  1128. assert retvalues[2] == "DodsworthAnne", '%s is not "DodsworthAnne"' % repr(
  1129. retvalues[2]
  1130. )
  1131. self.conn.rollback()
  1132. def testMultipleSetReturn(self):
  1133. crsr = self.getCursor()
  1134. self.helpCreateAndPopulateTableTemp(crsr)
  1135. spdef = """
  1136. CREATE PROCEDURE sp_DeleteMe_OnlyForTesting
  1137. AS
  1138. SELECT fldData FROM xx_%s ORDER BY fldData ASC
  1139. SELECT fldData From xx_%s where fldData = -9999
  1140. SELECT fldData FROM xx_%s ORDER BY fldData DESC
  1141. """ % (
  1142. config.tmp,
  1143. config.tmp,
  1144. config.tmp,
  1145. )
  1146. try:
  1147. crsr.execute("DROP PROCEDURE sp_DeleteMe_OnlyForTesting")
  1148. self.conn.commit()
  1149. except: # Make sure it is empty
  1150. pass
  1151. crsr.execute(spdef)
  1152. retvalues = crsr.callproc("sp_DeleteMe_OnlyForTesting")
  1153. row = crsr.fetchone()
  1154. self.assertEqual(row[0], 0)
  1155. assert crsr.nextset() == True, "Operation should succeed"
  1156. assert not crsr.fetchall(), "Should be an empty second set"
  1157. assert crsr.nextset() == True, "third set should be present"
  1158. rowdesc = crsr.fetchall()
  1159. self.assertEqual(rowdesc[0][0], 8)
  1160. assert crsr.nextset() == None, "No more return sets, should return None"
  1161. self.helpRollbackTblTemp()
  1162. def testDatetimeProcedureParameter(self):
  1163. crsr = self.conn.cursor()
  1164. spdef = """
  1165. CREATE PROCEDURE sp_DeleteMeOnlyForTesting
  1166. @theInput DATETIME,
  1167. @theOtherInput varchar(50),
  1168. @theOutput varchar(100) OUTPUT
  1169. AS
  1170. SET @theOutput = CONVERT(CHARACTER(20), @theInput, 0) + @theOtherInput
  1171. """
  1172. try:
  1173. crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting")
  1174. self.conn.commit()
  1175. except: # Make sure it is empty
  1176. pass
  1177. crsr.execute(spdef)
  1178. result = crsr.callproc(
  1179. "sp_DeleteMeOnlyForTesting",
  1180. [adodbapi.Timestamp(2014, 12, 25, 0, 1, 0), "Beep", " " * 30],
  1181. )
  1182. assert result[2] == "Dec 25 2014 12:01AM Beep", 'value was="%s"' % result[2]
  1183. self.conn.rollback()
  1184. def testIncorrectStoredProcedureParameter(self):
  1185. crsr = self.conn.cursor()
  1186. spdef = """
  1187. CREATE PROCEDURE sp_DeleteMeOnlyForTesting
  1188. @theInput DATETIME,
  1189. @theOtherInput varchar(50),
  1190. @theOutput varchar(100) OUTPUT
  1191. AS
  1192. SET @theOutput = CONVERT(CHARACTER(20), @theInput) + @theOtherInput
  1193. """
  1194. try:
  1195. crsr.execute("DROP PROCEDURE sp_DeleteMeOnlyForTesting")
  1196. self.conn.commit()
  1197. except: # Make sure it is empty
  1198. pass
  1199. crsr.execute(spdef)
  1200. # calling the sproc with a string for the first parameter where a DateTime is expected
  1201. result = tryconnection.try_operation_with_expected_exception(
  1202. (api.DataError, api.DatabaseError),
  1203. crsr.callproc,
  1204. ["sp_DeleteMeOnlyForTesting"],
  1205. {"parameters": ["this is wrong", "Anne", "not Alice"]},
  1206. )
  1207. if result[0]: # the expected exception was raised
  1208. assert "@theInput" in str(result[1]) or "DatabaseError" in str(
  1209. result
  1210. ), "Identifies the wrong erroneous parameter"
  1211. else:
  1212. assert result[0], result[1] # incorrect or no exception
  1213. self.conn.rollback()
  1214. class TestADOwithAccessDB(CommonDBTests):
  1215. def setUp(self):
  1216. self.conn = config.dbAccessconnect(
  1217. *config.connStrAccess[0], **config.connStrAccess[1]
  1218. )
  1219. self.conn.timeout = 30 # turn timeout back up
  1220. self.engine = "ACCESS"
  1221. self.db = config.dbAccessconnect
  1222. self.remote = config.connStrAccess[2]
  1223. def tearDown(self):
  1224. try:
  1225. self.conn.rollback()
  1226. except:
  1227. pass
  1228. try:
  1229. self.conn.close()
  1230. except:
  1231. pass
  1232. self.conn = None
  1233. def getConnection(self):
  1234. return self.conn
  1235. def getAnotherConnection(self, addkeys=None):
  1236. raise NotImplementedError("Jet cannot use a second connection to the database")
  1237. def testOkConnect(self):
  1238. c = self.db(*config.connStrAccess[0], **config.connStrAccess[1])
  1239. assert c != None
  1240. c.close()
  1241. class TestADOwithMySql(CommonDBTests):
  1242. def setUp(self):
  1243. self.conn = config.dbMySqlconnect(
  1244. *config.connStrMySql[0], **config.connStrMySql[1]
  1245. )
  1246. self.conn.timeout = 30 # turn timeout back up
  1247. self.engine = "MySQL"
  1248. self.db = config.dbMySqlconnect
  1249. self.remote = config.connStrMySql[2]
  1250. def tearDown(self):
  1251. try:
  1252. self.conn.rollback()
  1253. except:
  1254. pass
  1255. try:
  1256. self.conn.close()
  1257. except:
  1258. pass
  1259. self.conn = None
  1260. def getConnection(self):
  1261. return self.conn
  1262. def getAnotherConnection(self, addkeys=None):
  1263. keys = dict(config.connStrMySql[1])
  1264. if addkeys:
  1265. keys.update(addkeys)
  1266. return config.dbMySqlconnect(*config.connStrMySql[0], **keys)
  1267. def testOkConnect(self):
  1268. c = self.db(*config.connStrMySql[0], **config.connStrMySql[1])
  1269. assert c != None
  1270. # def testStoredProcedure(self):
  1271. # crsr=self.conn.cursor()
  1272. # try:
  1273. # crsr.execute("DROP PROCEDURE DeleteMeOnlyForTesting")
  1274. # self.conn.commit()
  1275. # except: #Make sure it is empty
  1276. # pass
  1277. # spdef= """
  1278. # DELIMITER $$
  1279. # CREATE PROCEDURE DeleteMeOnlyForTesting (onein CHAR(10), twoin CHAR(10), OUT theout CHAR(20))
  1280. # DETERMINISTIC
  1281. # BEGIN
  1282. # SET theout = onein //|| twoin;
  1283. # /* (SELECT 'a small string' as result; */
  1284. # END $$
  1285. # """
  1286. #
  1287. # crsr.execute(spdef)
  1288. #
  1289. # retvalues=crsr.callproc('DeleteMeOnlyForTesting',('Dodsworth','Anne',' '))
  1290. # print 'return value (mysql)=',repr(crsr.returnValue) ###
  1291. # assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0])
  1292. # assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1])
  1293. # assert retvalues[2]=='DodsworthAnne','%s is not "DodsworthAnne"'%repr(retvalues[2])
  1294. #
  1295. # try:
  1296. # crsr.execute("DROP PROCEDURE, DeleteMeOnlyForTesting")
  1297. # self.conn.commit()
  1298. # except: #Make sure it is empty
  1299. # pass
  1300. class TestADOwithPostgres(CommonDBTests):
  1301. def setUp(self):
  1302. self.conn = config.dbPostgresConnect(
  1303. *config.connStrPostgres[0], **config.connStrPostgres[1]
  1304. )
  1305. self.conn.timeout = 30 # turn timeout back up
  1306. self.engine = "PostgreSQL"
  1307. self.db = config.dbPostgresConnect
  1308. self.remote = config.connStrPostgres[2]
  1309. def tearDown(self):
  1310. try:
  1311. self.conn.rollback()
  1312. except:
  1313. pass
  1314. try:
  1315. self.conn.close()
  1316. except:
  1317. pass
  1318. self.conn = None
  1319. def getConnection(self):
  1320. return self.conn
  1321. def getAnotherConnection(self, addkeys=None):
  1322. keys = dict(config.connStrPostgres[1])
  1323. if addkeys:
  1324. keys.update(addkeys)
  1325. return config.dbPostgresConnect(*config.connStrPostgres[0], **keys)
  1326. def testOkConnect(self):
  1327. c = self.db(*config.connStrPostgres[0], **config.connStrPostgres[1])
  1328. assert c != None
  1329. # def testStoredProcedure(self):
  1330. # crsr=self.conn.cursor()
  1331. # spdef= """
  1332. # CREATE OR REPLACE FUNCTION DeleteMeOnlyForTesting (text, text)
  1333. # RETURNS text AS $funk$
  1334. # BEGIN
  1335. # RETURN $1 || $2;
  1336. # END;
  1337. # $funk$
  1338. # LANGUAGE SQL;
  1339. # """
  1340. #
  1341. # crsr.execute(spdef)
  1342. # retvalues = crsr.callproc('DeleteMeOnlyForTesting',('Dodsworth','Anne',' '))
  1343. # ### print 'return value (pg)=',repr(crsr.returnValue) ###
  1344. # assert retvalues[0]=='Dodsworth', '%s is not "Dodsworth"'%repr(retvalues[0])
  1345. # assert retvalues[1]=='Anne','%s is not "Anne"'%repr(retvalues[1])
  1346. # assert retvalues[2]=='Dodsworth Anne','%s is not "Dodsworth Anne"'%repr(retvalues[2])
  1347. # self.conn.rollback()
  1348. # try:
  1349. # crsr.execute("DROP PROCEDURE, DeleteMeOnlyForTesting")
  1350. # self.conn.commit()
  1351. # except: #Make sure it is empty
  1352. # pass
  1353. class TimeConverterInterfaceTest(unittest.TestCase):
  1354. def testIDate(self):
  1355. assert self.tc.Date(1990, 2, 2)
  1356. def testITime(self):
  1357. assert self.tc.Time(13, 2, 2)
  1358. def testITimestamp(self):
  1359. assert self.tc.Timestamp(1990, 2, 2, 13, 2, 1)
  1360. def testIDateObjectFromCOMDate(self):
  1361. assert self.tc.DateObjectFromCOMDate(37435.7604282)
  1362. def testICOMDate(self):
  1363. assert hasattr(self.tc, "COMDate")
  1364. def testExactDate(self):
  1365. d = self.tc.Date(1994, 11, 15)
  1366. comDate = self.tc.COMDate(d)
  1367. correct = 34653.0
  1368. assert comDate == correct, comDate
  1369. def testExactTimestamp(self):
  1370. d = self.tc.Timestamp(1994, 11, 15, 12, 0, 0)
  1371. comDate = self.tc.COMDate(d)
  1372. correct = 34653.5
  1373. self.assertEqual(comDate, correct)
  1374. d = self.tc.Timestamp(2003, 5, 6, 14, 15, 17)
  1375. comDate = self.tc.COMDate(d)
  1376. correct = 37747.593946759262
  1377. self.assertEqual(comDate, correct)
  1378. def testIsoFormat(self):
  1379. d = self.tc.Timestamp(1994, 11, 15, 12, 3, 10)
  1380. iso = self.tc.DateObjectToIsoFormatString(d)
  1381. self.assertEqual(str(iso[:19]), "1994-11-15 12:03:10")
  1382. dt = self.tc.Date(2003, 5, 2)
  1383. iso = self.tc.DateObjectToIsoFormatString(dt)
  1384. self.assertEqual(str(iso[:10]), "2003-05-02")
  1385. if config.doMxDateTimeTest:
  1386. import mx.DateTime
  1387. class TestMXDateTimeConverter(TimeConverterInterfaceTest):
  1388. def setUp(self):
  1389. self.tc = api.mxDateTimeConverter()
  1390. def testCOMDate(self):
  1391. t = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 2)
  1392. cmd = self.tc.COMDate(t)
  1393. assert cmd == t.COMDate()
  1394. def testDateObjectFromCOMDate(self):
  1395. cmd = self.tc.DateObjectFromCOMDate(37435.7604282)
  1396. t = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 0)
  1397. t2 = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 2)
  1398. assert t2 > cmd > t
  1399. def testDate(self):
  1400. assert mx.DateTime.Date(1980, 11, 4) == self.tc.Date(1980, 11, 4)
  1401. def testTime(self):
  1402. assert mx.DateTime.Time(13, 11, 4) == self.tc.Time(13, 11, 4)
  1403. def testTimestamp(self):
  1404. t = mx.DateTime.DateTime(2002, 6, 28, 18, 15, 1)
  1405. obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 1)
  1406. assert t == obj
  1407. import time
  1408. class TestPythonTimeConverter(TimeConverterInterfaceTest):
  1409. def setUp(self):
  1410. self.tc = api.pythonTimeConverter()
  1411. def testCOMDate(self):
  1412. mk = time.mktime((2002, 6, 28, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1))
  1413. t = time.localtime(mk)
  1414. # Fri, 28 Jun 2002 18:15:01 +0000
  1415. cmd = self.tc.COMDate(t)
  1416. assert abs(cmd - 37435.7604282) < 1.0 / 24, "%f more than an hour wrong" % cmd
  1417. def testDateObjectFromCOMDate(self):
  1418. cmd = self.tc.DateObjectFromCOMDate(37435.7604282)
  1419. t1 = time.gmtime(
  1420. time.mktime((2002, 6, 28, 0, 14, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1))
  1421. )
  1422. # there are errors in the implementation of gmtime which we ignore
  1423. t2 = time.gmtime(
  1424. time.mktime((2002, 6, 29, 12, 14, 2, 4, 31 + 28 + 31 + 30 + 31 + 28, -1))
  1425. )
  1426. assert t1 < cmd < t2, '"%s" should be about 2002-6-28 12:15:01' % repr(cmd)
  1427. def testDate(self):
  1428. t1 = time.mktime((2002, 6, 28, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 30, 0))
  1429. t2 = time.mktime((2002, 6, 30, 18, 15, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, 0))
  1430. obj = self.tc.Date(2002, 6, 29)
  1431. assert t1 < time.mktime(obj) < t2, obj
  1432. def testTime(self):
  1433. self.assertEqual(
  1434. self.tc.Time(18, 15, 2), time.gmtime(18 * 60 * 60 + 15 * 60 + 2)
  1435. )
  1436. def testTimestamp(self):
  1437. t1 = time.localtime(
  1438. time.mktime((2002, 6, 28, 18, 14, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1))
  1439. )
  1440. t2 = time.localtime(
  1441. time.mktime((2002, 6, 28, 18, 16, 1, 4, 31 + 28 + 31 + 30 + 31 + 28, -1))
  1442. )
  1443. obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 2)
  1444. assert t1 < obj < t2, obj
  1445. class TestPythonDateTimeConverter(TimeConverterInterfaceTest):
  1446. def setUp(self):
  1447. self.tc = api.pythonDateTimeConverter()
  1448. def testCOMDate(self):
  1449. t = datetime.datetime(2002, 6, 28, 18, 15, 1)
  1450. # Fri, 28 Jun 2002 18:15:01 +0000
  1451. cmd = self.tc.COMDate(t)
  1452. assert abs(cmd - 37435.7604282) < 1.0 / 24, "more than an hour wrong"
  1453. def testDateObjectFromCOMDate(self):
  1454. cmd = self.tc.DateObjectFromCOMDate(37435.7604282)
  1455. t1 = datetime.datetime(2002, 6, 28, 18, 14, 1)
  1456. t2 = datetime.datetime(2002, 6, 28, 18, 16, 1)
  1457. assert t1 < cmd < t2, cmd
  1458. tx = datetime.datetime(
  1459. 2002, 6, 28, 18, 14, 1, 900000
  1460. ) # testing that microseconds don't become milliseconds
  1461. c1 = self.tc.DateObjectFromCOMDate(self.tc.COMDate(tx))
  1462. assert t1 < c1 < t2, c1
  1463. def testDate(self):
  1464. t1 = datetime.date(2002, 6, 28)
  1465. t2 = datetime.date(2002, 6, 30)
  1466. obj = self.tc.Date(2002, 6, 29)
  1467. assert t1 < obj < t2, obj
  1468. def testTime(self):
  1469. self.assertEqual(self.tc.Time(18, 15, 2).isoformat()[:8], "18:15:02")
  1470. def testTimestamp(self):
  1471. t1 = datetime.datetime(2002, 6, 28, 18, 14, 1)
  1472. t2 = datetime.datetime(2002, 6, 28, 18, 16, 1)
  1473. obj = self.tc.Timestamp(2002, 6, 28, 18, 15, 2)
  1474. assert t1 < obj < t2, obj
  1475. suites = []
  1476. suites.append(unittest.makeSuite(TestPythonDateTimeConverter, "test"))
  1477. if config.doMxDateTimeTest:
  1478. suites.append(unittest.makeSuite(TestMXDateTimeConverter, "test"))
  1479. if config.doTimeTest:
  1480. suites.append(unittest.makeSuite(TestPythonTimeConverter, "test"))
  1481. if config.doAccessTest:
  1482. suites.append(unittest.makeSuite(TestADOwithAccessDB, "test"))
  1483. if config.doSqlServerTest:
  1484. suites.append(unittest.makeSuite(TestADOwithSQLServer, "test"))
  1485. if config.doMySqlTest:
  1486. suites.append(unittest.makeSuite(TestADOwithMySql, "test"))
  1487. if config.doPostgresTest:
  1488. suites.append(unittest.makeSuite(TestADOwithPostgres, "test"))
  1489. class cleanup_manager(object):
  1490. def __enter__(self):
  1491. pass
  1492. def __exit__(self, exc_type, exc_val, exc_tb):
  1493. config.cleanup(config.testfolder, config.mdb_name)
  1494. suite = unittest.TestSuite(suites)
  1495. if __name__ == "__main__":
  1496. mysuite = copy.deepcopy(suite)
  1497. with cleanup_manager():
  1498. defaultDateConverter = adodbapi.dateconverter
  1499. print(__doc__)
  1500. print("Default Date Converter is %s" % (defaultDateConverter,))
  1501. dateconverter = defaultDateConverter
  1502. tag = "datetime"
  1503. unittest.TextTestRunner().run(mysuite)
  1504. if config.iterateOverTimeTests:
  1505. for test, dateconverter, tag in (
  1506. (config.doTimeTest, api.pythonTimeConverter, "pythontime"),
  1507. (config.doMxDateTimeTest, api.mxDateTimeConverter, "mx"),
  1508. ):
  1509. if test:
  1510. mysuite = copy.deepcopy(
  1511. suite
  1512. ) # work around a side effect of unittest.TextTestRunner
  1513. adodbapi.adodbapi.dateconverter = dateconverter()
  1514. print("Changed dateconverter to ")
  1515. print(adodbapi.adodbapi.dateconverter)
  1516. unittest.TextTestRunner().run(mysuite)