base.py 187 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378
  1. # dialects/postgresql/base.py
  2. # Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. # mypy: ignore-errors
  8. r"""
  9. .. dialect:: postgresql
  10. :name: PostgreSQL
  11. :normal_support: 9.6+
  12. :best_effort: 9+
  13. .. _postgresql_sequences:
  14. Sequences/SERIAL/IDENTITY
  15. -------------------------
  16. PostgreSQL supports sequences, and SQLAlchemy uses these as the default means
  17. of creating new primary key values for integer-based primary key columns. When
  18. creating tables, SQLAlchemy will issue the ``SERIAL`` datatype for
  19. integer-based primary key columns, which generates a sequence and server side
  20. default corresponding to the column.
  21. To specify a specific named sequence to be used for primary key generation,
  22. use the :func:`~sqlalchemy.schema.Sequence` construct::
  23. Table(
  24. "sometable",
  25. metadata,
  26. Column(
  27. "id", Integer, Sequence("some_id_seq", start=1), primary_key=True
  28. ),
  29. )
  30. When SQLAlchemy issues a single INSERT statement, to fulfill the contract of
  31. having the "last insert identifier" available, a RETURNING clause is added to
  32. the INSERT statement which specifies the primary key columns should be
  33. returned after the statement completes. The RETURNING functionality only takes
  34. place if PostgreSQL 8.2 or later is in use. As a fallback approach, the
  35. sequence, whether specified explicitly or implicitly via ``SERIAL``, is
  36. executed independently beforehand, the returned value to be used in the
  37. subsequent insert. Note that when an
  38. :func:`~sqlalchemy.sql.expression.insert()` construct is executed using
  39. "executemany" semantics, the "last inserted identifier" functionality does not
  40. apply; no RETURNING clause is emitted nor is the sequence pre-executed in this
  41. case.
  42. PostgreSQL 10 and above IDENTITY columns
  43. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  44. PostgreSQL 10 and above have a new IDENTITY feature that supersedes the use
  45. of SERIAL. The :class:`_schema.Identity` construct in a
  46. :class:`_schema.Column` can be used to control its behavior::
  47. from sqlalchemy import Table, Column, MetaData, Integer, Computed
  48. metadata = MetaData()
  49. data = Table(
  50. "data",
  51. metadata,
  52. Column(
  53. "id", Integer, Identity(start=42, cycle=True), primary_key=True
  54. ),
  55. Column("data", String),
  56. )
  57. The CREATE TABLE for the above :class:`_schema.Table` object would be:
  58. .. sourcecode:: sql
  59. CREATE TABLE data (
  60. id INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 42 CYCLE),
  61. data VARCHAR,
  62. PRIMARY KEY (id)
  63. )
  64. .. versionchanged:: 1.4 Added :class:`_schema.Identity` construct
  65. in a :class:`_schema.Column` to specify the option of an autoincrementing
  66. column.
  67. .. note::
  68. Previous versions of SQLAlchemy did not have built-in support for rendering
  69. of IDENTITY, and could use the following compilation hook to replace
  70. occurrences of SERIAL with IDENTITY::
  71. from sqlalchemy.schema import CreateColumn
  72. from sqlalchemy.ext.compiler import compiles
  73. @compiles(CreateColumn, "postgresql")
  74. def use_identity(element, compiler, **kw):
  75. text = compiler.visit_create_column(element, **kw)
  76. text = text.replace("SERIAL", "INT GENERATED BY DEFAULT AS IDENTITY")
  77. return text
  78. Using the above, a table such as::
  79. t = Table(
  80. "t", m, Column("id", Integer, primary_key=True), Column("data", String)
  81. )
  82. Will generate on the backing database as:
  83. .. sourcecode:: sql
  84. CREATE TABLE t (
  85. id INT GENERATED BY DEFAULT AS IDENTITY,
  86. data VARCHAR,
  87. PRIMARY KEY (id)
  88. )
  89. .. _postgresql_ss_cursors:
  90. Server Side Cursors
  91. -------------------
  92. Server-side cursor support is available for the psycopg2, asyncpg
  93. dialects and may also be available in others.
  94. Server side cursors are enabled on a per-statement basis by using the
  95. :paramref:`.Connection.execution_options.stream_results` connection execution
  96. option::
  97. with engine.connect() as conn:
  98. result = conn.execution_options(stream_results=True).execute(
  99. text("select * from table")
  100. )
  101. Note that some kinds of SQL statements may not be supported with
  102. server side cursors; generally, only SQL statements that return rows should be
  103. used with this option.
  104. .. deprecated:: 1.4 The dialect-level server_side_cursors flag is deprecated
  105. and will be removed in a future release. Please use the
  106. :paramref:`_engine.Connection.stream_results` execution option for
  107. unbuffered cursor support.
  108. .. seealso::
  109. :ref:`engine_stream_results`
  110. .. _postgresql_isolation_level:
  111. Transaction Isolation Level
  112. ---------------------------
  113. Most SQLAlchemy dialects support setting of transaction isolation level
  114. using the :paramref:`_sa.create_engine.isolation_level` parameter
  115. at the :func:`_sa.create_engine` level, and at the :class:`_engine.Connection`
  116. level via the :paramref:`.Connection.execution_options.isolation_level`
  117. parameter.
  118. For PostgreSQL dialects, this feature works either by making use of the
  119. DBAPI-specific features, such as psycopg2's isolation level flags which will
  120. embed the isolation level setting inline with the ``"BEGIN"`` statement, or for
  121. DBAPIs with no direct support by emitting ``SET SESSION CHARACTERISTICS AS
  122. TRANSACTION ISOLATION LEVEL <level>`` ahead of the ``"BEGIN"`` statement
  123. emitted by the DBAPI. For the special AUTOCOMMIT isolation level,
  124. DBAPI-specific techniques are used which is typically an ``.autocommit``
  125. flag on the DBAPI connection object.
  126. To set isolation level using :func:`_sa.create_engine`::
  127. engine = create_engine(
  128. "postgresql+pg8000://scott:tiger@localhost/test",
  129. isolation_level="REPEATABLE READ",
  130. )
  131. To set using per-connection execution options::
  132. with engine.connect() as conn:
  133. conn = conn.execution_options(isolation_level="REPEATABLE READ")
  134. with conn.begin():
  135. ... # work with transaction
  136. There are also more options for isolation level configurations, such as
  137. "sub-engine" objects linked to a main :class:`_engine.Engine` which each apply
  138. different isolation level settings. See the discussion at
  139. :ref:`dbapi_autocommit` for background.
  140. Valid values for ``isolation_level`` on most PostgreSQL dialects include:
  141. * ``READ COMMITTED``
  142. * ``READ UNCOMMITTED``
  143. * ``REPEATABLE READ``
  144. * ``SERIALIZABLE``
  145. * ``AUTOCOMMIT``
  146. .. seealso::
  147. :ref:`dbapi_autocommit`
  148. :ref:`postgresql_readonly_deferrable`
  149. :ref:`psycopg2_isolation_level`
  150. :ref:`pg8000_isolation_level`
  151. .. _postgresql_readonly_deferrable:
  152. Setting READ ONLY / DEFERRABLE
  153. ------------------------------
  154. Most PostgreSQL dialects support setting the "READ ONLY" and "DEFERRABLE"
  155. characteristics of the transaction, which is in addition to the isolation level
  156. setting. These two attributes can be established either in conjunction with or
  157. independently of the isolation level by passing the ``postgresql_readonly`` and
  158. ``postgresql_deferrable`` flags with
  159. :meth:`_engine.Connection.execution_options`. The example below illustrates
  160. passing the ``"SERIALIZABLE"`` isolation level at the same time as setting
  161. "READ ONLY" and "DEFERRABLE"::
  162. with engine.connect() as conn:
  163. conn = conn.execution_options(
  164. isolation_level="SERIALIZABLE",
  165. postgresql_readonly=True,
  166. postgresql_deferrable=True,
  167. )
  168. with conn.begin():
  169. ... # work with transaction
  170. Note that some DBAPIs such as asyncpg only support "readonly" with
  171. SERIALIZABLE isolation.
  172. .. versionadded:: 1.4 added support for the ``postgresql_readonly``
  173. and ``postgresql_deferrable`` execution options.
  174. .. _postgresql_reset_on_return:
  175. Temporary Table / Resource Reset for Connection Pooling
  176. -------------------------------------------------------
  177. The :class:`.QueuePool` connection pool implementation used
  178. by the SQLAlchemy :class:`.Engine` object includes
  179. :ref:`reset on return <pool_reset_on_return>` behavior that will invoke
  180. the DBAPI ``.rollback()`` method when connections are returned to the pool.
  181. While this rollback will clear out the immediate state used by the previous
  182. transaction, it does not cover a wider range of session-level state, including
  183. temporary tables as well as other server state such as prepared statement
  184. handles and statement caches. The PostgreSQL database includes a variety
  185. of commands which may be used to reset this state, including
  186. ``DISCARD``, ``RESET``, ``DEALLOCATE``, and ``UNLISTEN``.
  187. To install
  188. one or more of these commands as the means of performing reset-on-return,
  189. the :meth:`.PoolEvents.reset` event hook may be used, as demonstrated
  190. in the example below. The implementation
  191. will end transactions in progress as well as discard temporary tables
  192. using the ``CLOSE``, ``RESET`` and ``DISCARD`` commands; see the PostgreSQL
  193. documentation for background on what each of these statements do.
  194. The :paramref:`_sa.create_engine.pool_reset_on_return` parameter
  195. is set to ``None`` so that the custom scheme can replace the default behavior
  196. completely. The custom hook implementation calls ``.rollback()`` in any case,
  197. as it's usually important that the DBAPI's own tracking of commit/rollback
  198. will remain consistent with the state of the transaction::
  199. from sqlalchemy import create_engine
  200. from sqlalchemy import event
  201. postgresql_engine = create_engine(
  202. "postgresql+psycopg2://scott:tiger@hostname/dbname",
  203. # disable default reset-on-return scheme
  204. pool_reset_on_return=None,
  205. )
  206. @event.listens_for(postgresql_engine, "reset")
  207. def _reset_postgresql(dbapi_connection, connection_record, reset_state):
  208. if not reset_state.terminate_only:
  209. dbapi_connection.execute("CLOSE ALL")
  210. dbapi_connection.execute("RESET ALL")
  211. dbapi_connection.execute("DISCARD TEMP")
  212. # so that the DBAPI itself knows that the connection has been
  213. # reset
  214. dbapi_connection.rollback()
  215. .. versionchanged:: 2.0.0b3 Added additional state arguments to
  216. the :meth:`.PoolEvents.reset` event and additionally ensured the event
  217. is invoked for all "reset" occurrences, so that it's appropriate
  218. as a place for custom "reset" handlers. Previous schemes which
  219. use the :meth:`.PoolEvents.checkin` handler remain usable as well.
  220. .. seealso::
  221. :ref:`pool_reset_on_return` - in the :ref:`pooling_toplevel` documentation
  222. .. _postgresql_alternate_search_path:
  223. Setting Alternate Search Paths on Connect
  224. ------------------------------------------
  225. The PostgreSQL ``search_path`` variable refers to the list of schema names
  226. that will be implicitly referenced when a particular table or other
  227. object is referenced in a SQL statement. As detailed in the next section
  228. :ref:`postgresql_schema_reflection`, SQLAlchemy is generally organized around
  229. the concept of keeping this variable at its default value of ``public``,
  230. however, in order to have it set to any arbitrary name or names when connections
  231. are used automatically, the "SET SESSION search_path" command may be invoked
  232. for all connections in a pool using the following event handler, as discussed
  233. at :ref:`schema_set_default_connections`::
  234. from sqlalchemy import event
  235. from sqlalchemy import create_engine
  236. engine = create_engine("postgresql+psycopg2://scott:tiger@host/dbname")
  237. @event.listens_for(engine, "connect", insert=True)
  238. def set_search_path(dbapi_connection, connection_record):
  239. existing_autocommit = dbapi_connection.autocommit
  240. dbapi_connection.autocommit = True
  241. cursor = dbapi_connection.cursor()
  242. cursor.execute("SET SESSION search_path='%s'" % schema_name)
  243. cursor.close()
  244. dbapi_connection.autocommit = existing_autocommit
  245. The reason the recipe is complicated by use of the ``.autocommit`` DBAPI
  246. attribute is so that when the ``SET SESSION search_path`` directive is invoked,
  247. it is invoked outside of the scope of any transaction and therefore will not
  248. be reverted when the DBAPI connection has a rollback.
  249. .. seealso::
  250. :ref:`schema_set_default_connections` - in the :ref:`metadata_toplevel` documentation
  251. .. _postgresql_schema_reflection:
  252. Remote-Schema Table Introspection and PostgreSQL search_path
  253. ------------------------------------------------------------
  254. .. admonition:: Section Best Practices Summarized
  255. keep the ``search_path`` variable set to its default of ``public``, without
  256. any other schema names. Ensure the username used to connect **does not**
  257. match remote schemas, or ensure the ``"$user"`` token is **removed** from
  258. ``search_path``. For other schema names, name these explicitly
  259. within :class:`_schema.Table` definitions. Alternatively, the
  260. ``postgresql_ignore_search_path`` option will cause all reflected
  261. :class:`_schema.Table` objects to have a :attr:`_schema.Table.schema`
  262. attribute set up.
  263. The PostgreSQL dialect can reflect tables from any schema, as outlined in
  264. :ref:`metadata_reflection_schemas`.
  265. In all cases, the first thing SQLAlchemy does when reflecting tables is
  266. to **determine the default schema for the current database connection**.
  267. It does this using the PostgreSQL ``current_schema()``
  268. function, illustated below using a PostgreSQL client session (i.e. using
  269. the ``psql`` tool):
  270. .. sourcecode:: sql
  271. test=> select current_schema();
  272. current_schema
  273. ----------------
  274. public
  275. (1 row)
  276. Above we see that on a plain install of PostgreSQL, the default schema name
  277. is the name ``public``.
  278. However, if your database username **matches the name of a schema**, PostgreSQL's
  279. default is to then **use that name as the default schema**. Below, we log in
  280. using the username ``scott``. When we create a schema named ``scott``, **it
  281. implicitly changes the default schema**:
  282. .. sourcecode:: sql
  283. test=> select current_schema();
  284. current_schema
  285. ----------------
  286. public
  287. (1 row)
  288. test=> create schema scott;
  289. CREATE SCHEMA
  290. test=> select current_schema();
  291. current_schema
  292. ----------------
  293. scott
  294. (1 row)
  295. The behavior of ``current_schema()`` is derived from the
  296. `PostgreSQL search path
  297. <https://www.postgresql.org/docs/current/static/ddl-schemas.html#DDL-SCHEMAS-PATH>`_
  298. variable ``search_path``, which in modern PostgreSQL versions defaults to this:
  299. .. sourcecode:: sql
  300. test=> show search_path;
  301. search_path
  302. -----------------
  303. "$user", public
  304. (1 row)
  305. Where above, the ``"$user"`` variable will inject the current username as the
  306. default schema, if one exists. Otherwise, ``public`` is used.
  307. When a :class:`_schema.Table` object is reflected, if it is present in the
  308. schema indicated by the ``current_schema()`` function, **the schema name assigned
  309. to the ".schema" attribute of the Table is the Python "None" value**. Otherwise, the
  310. ".schema" attribute will be assigned the string name of that schema.
  311. With regards to tables which these :class:`_schema.Table`
  312. objects refer to via foreign key constraint, a decision must be made as to how
  313. the ``.schema`` is represented in those remote tables, in the case where that
  314. remote schema name is also a member of the current ``search_path``.
  315. By default, the PostgreSQL dialect mimics the behavior encouraged by
  316. PostgreSQL's own ``pg_get_constraintdef()`` builtin procedure. This function
  317. returns a sample definition for a particular foreign key constraint,
  318. omitting the referenced schema name from that definition when the name is
  319. also in the PostgreSQL schema search path. The interaction below
  320. illustrates this behavior:
  321. .. sourcecode:: sql
  322. test=> CREATE TABLE test_schema.referred(id INTEGER PRIMARY KEY);
  323. CREATE TABLE
  324. test=> CREATE TABLE referring(
  325. test(> id INTEGER PRIMARY KEY,
  326. test(> referred_id INTEGER REFERENCES test_schema.referred(id));
  327. CREATE TABLE
  328. test=> SET search_path TO public, test_schema;
  329. test=> SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM
  330. test-> pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n
  331. test-> ON n.oid = c.relnamespace
  332. test-> JOIN pg_catalog.pg_constraint r ON c.oid = r.conrelid
  333. test-> WHERE c.relname='referring' AND r.contype = 'f'
  334. test-> ;
  335. pg_get_constraintdef
  336. ---------------------------------------------------
  337. FOREIGN KEY (referred_id) REFERENCES referred(id)
  338. (1 row)
  339. Above, we created a table ``referred`` as a member of the remote schema
  340. ``test_schema``, however when we added ``test_schema`` to the
  341. PG ``search_path`` and then asked ``pg_get_constraintdef()`` for the
  342. ``FOREIGN KEY`` syntax, ``test_schema`` was not included in the output of
  343. the function.
  344. On the other hand, if we set the search path back to the typical default
  345. of ``public``:
  346. .. sourcecode:: sql
  347. test=> SET search_path TO public;
  348. SET
  349. The same query against ``pg_get_constraintdef()`` now returns the fully
  350. schema-qualified name for us:
  351. .. sourcecode:: sql
  352. test=> SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM
  353. test-> pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n
  354. test-> ON n.oid = c.relnamespace
  355. test-> JOIN pg_catalog.pg_constraint r ON c.oid = r.conrelid
  356. test-> WHERE c.relname='referring' AND r.contype = 'f';
  357. pg_get_constraintdef
  358. ---------------------------------------------------------------
  359. FOREIGN KEY (referred_id) REFERENCES test_schema.referred(id)
  360. (1 row)
  361. SQLAlchemy will by default use the return value of ``pg_get_constraintdef()``
  362. in order to determine the remote schema name. That is, if our ``search_path``
  363. were set to include ``test_schema``, and we invoked a table
  364. reflection process as follows::
  365. >>> from sqlalchemy import Table, MetaData, create_engine, text
  366. >>> engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
  367. >>> with engine.connect() as conn:
  368. ... conn.execute(text("SET search_path TO test_schema, public"))
  369. ... metadata_obj = MetaData()
  370. ... referring = Table("referring", metadata_obj, autoload_with=conn)
  371. <sqlalchemy.engine.result.CursorResult object at 0x101612ed0>
  372. The above process would deliver to the :attr:`_schema.MetaData.tables`
  373. collection
  374. ``referred`` table named **without** the schema::
  375. >>> metadata_obj.tables["referred"].schema is None
  376. True
  377. To alter the behavior of reflection such that the referred schema is
  378. maintained regardless of the ``search_path`` setting, use the
  379. ``postgresql_ignore_search_path`` option, which can be specified as a
  380. dialect-specific argument to both :class:`_schema.Table` as well as
  381. :meth:`_schema.MetaData.reflect`::
  382. >>> with engine.connect() as conn:
  383. ... conn.execute(text("SET search_path TO test_schema, public"))
  384. ... metadata_obj = MetaData()
  385. ... referring = Table(
  386. ... "referring",
  387. ... metadata_obj,
  388. ... autoload_with=conn,
  389. ... postgresql_ignore_search_path=True,
  390. ... )
  391. <sqlalchemy.engine.result.CursorResult object at 0x1016126d0>
  392. We will now have ``test_schema.referred`` stored as schema-qualified::
  393. >>> metadata_obj.tables["test_schema.referred"].schema
  394. 'test_schema'
  395. .. sidebar:: Best Practices for PostgreSQL Schema reflection
  396. The description of PostgreSQL schema reflection behavior is complex, and
  397. is the product of many years of dealing with widely varied use cases and
  398. user preferences. But in fact, there's no need to understand any of it if
  399. you just stick to the simplest use pattern: leave the ``search_path`` set
  400. to its default of ``public`` only, never refer to the name ``public`` as
  401. an explicit schema name otherwise, and refer to all other schema names
  402. explicitly when building up a :class:`_schema.Table` object. The options
  403. described here are only for those users who can't, or prefer not to, stay
  404. within these guidelines.
  405. .. seealso::
  406. :ref:`reflection_schema_qualified_interaction` - discussion of the issue
  407. from a backend-agnostic perspective
  408. `The Schema Search Path
  409. <https://www.postgresql.org/docs/current/static/ddl-schemas.html#DDL-SCHEMAS-PATH>`_
  410. - on the PostgreSQL website.
  411. INSERT/UPDATE...RETURNING
  412. -------------------------
  413. The dialect supports PG 8.2's ``INSERT..RETURNING``, ``UPDATE..RETURNING`` and
  414. ``DELETE..RETURNING`` syntaxes. ``INSERT..RETURNING`` is used by default
  415. for single-row INSERT statements in order to fetch newly generated
  416. primary key identifiers. To specify an explicit ``RETURNING`` clause,
  417. use the :meth:`._UpdateBase.returning` method on a per-statement basis::
  418. # INSERT..RETURNING
  419. result = (
  420. table.insert().returning(table.c.col1, table.c.col2).values(name="foo")
  421. )
  422. print(result.fetchall())
  423. # UPDATE..RETURNING
  424. result = (
  425. table.update()
  426. .returning(table.c.col1, table.c.col2)
  427. .where(table.c.name == "foo")
  428. .values(name="bar")
  429. )
  430. print(result.fetchall())
  431. # DELETE..RETURNING
  432. result = (
  433. table.delete()
  434. .returning(table.c.col1, table.c.col2)
  435. .where(table.c.name == "foo")
  436. )
  437. print(result.fetchall())
  438. .. _postgresql_insert_on_conflict:
  439. INSERT...ON CONFLICT (Upsert)
  440. ------------------------------
  441. Starting with version 9.5, PostgreSQL allows "upserts" (update or insert) of
  442. rows into a table via the ``ON CONFLICT`` clause of the ``INSERT`` statement. A
  443. candidate row will only be inserted if that row does not violate any unique
  444. constraints. In the case of a unique constraint violation, a secondary action
  445. can occur which can be either "DO UPDATE", indicating that the data in the
  446. target row should be updated, or "DO NOTHING", which indicates to silently skip
  447. this row.
  448. Conflicts are determined using existing unique constraints and indexes. These
  449. constraints may be identified either using their name as stated in DDL,
  450. or they may be inferred by stating the columns and conditions that comprise
  451. the indexes.
  452. SQLAlchemy provides ``ON CONFLICT`` support via the PostgreSQL-specific
  453. :func:`_postgresql.insert()` function, which provides
  454. the generative methods :meth:`_postgresql.Insert.on_conflict_do_update`
  455. and :meth:`~.postgresql.Insert.on_conflict_do_nothing`:
  456. .. sourcecode:: pycon+sql
  457. >>> from sqlalchemy.dialects.postgresql import insert
  458. >>> insert_stmt = insert(my_table).values(
  459. ... id="some_existing_id", data="inserted value"
  460. ... )
  461. >>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["id"])
  462. >>> print(do_nothing_stmt)
  463. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  464. ON CONFLICT (id) DO NOTHING
  465. {stop}
  466. >>> do_update_stmt = insert_stmt.on_conflict_do_update(
  467. ... constraint="pk_my_table", set_=dict(data="updated value")
  468. ... )
  469. >>> print(do_update_stmt)
  470. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  471. ON CONFLICT ON CONSTRAINT pk_my_table DO UPDATE SET data = %(param_1)s
  472. .. seealso::
  473. `INSERT .. ON CONFLICT
  474. <https://www.postgresql.org/docs/current/static/sql-insert.html#SQL-ON-CONFLICT>`_
  475. - in the PostgreSQL documentation.
  476. Specifying the Target
  477. ^^^^^^^^^^^^^^^^^^^^^
  478. Both methods supply the "target" of the conflict using either the
  479. named constraint or by column inference:
  480. * The :paramref:`_postgresql.Insert.on_conflict_do_update.index_elements` argument
  481. specifies a sequence containing string column names, :class:`_schema.Column`
  482. objects, and/or SQL expression elements, which would identify a unique
  483. index:
  484. .. sourcecode:: pycon+sql
  485. >>> do_update_stmt = insert_stmt.on_conflict_do_update(
  486. ... index_elements=["id"], set_=dict(data="updated value")
  487. ... )
  488. >>> print(do_update_stmt)
  489. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  490. ON CONFLICT (id) DO UPDATE SET data = %(param_1)s
  491. {stop}
  492. >>> do_update_stmt = insert_stmt.on_conflict_do_update(
  493. ... index_elements=[my_table.c.id], set_=dict(data="updated value")
  494. ... )
  495. >>> print(do_update_stmt)
  496. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  497. ON CONFLICT (id) DO UPDATE SET data = %(param_1)s
  498. * When using :paramref:`_postgresql.Insert.on_conflict_do_update.index_elements` to
  499. infer an index, a partial index can be inferred by also specifying the
  500. use the :paramref:`_postgresql.Insert.on_conflict_do_update.index_where` parameter:
  501. .. sourcecode:: pycon+sql
  502. >>> stmt = insert(my_table).values(user_email="a@b.com", data="inserted data")
  503. >>> stmt = stmt.on_conflict_do_update(
  504. ... index_elements=[my_table.c.user_email],
  505. ... index_where=my_table.c.user_email.like("%@gmail.com"),
  506. ... set_=dict(data=stmt.excluded.data),
  507. ... )
  508. >>> print(stmt)
  509. {printsql}INSERT INTO my_table (data, user_email)
  510. VALUES (%(data)s, %(user_email)s) ON CONFLICT (user_email)
  511. WHERE user_email LIKE %(user_email_1)s DO UPDATE SET data = excluded.data
  512. * The :paramref:`_postgresql.Insert.on_conflict_do_update.constraint` argument is
  513. used to specify an index directly rather than inferring it. This can be
  514. the name of a UNIQUE constraint, a PRIMARY KEY constraint, or an INDEX:
  515. .. sourcecode:: pycon+sql
  516. >>> do_update_stmt = insert_stmt.on_conflict_do_update(
  517. ... constraint="my_table_idx_1", set_=dict(data="updated value")
  518. ... )
  519. >>> print(do_update_stmt)
  520. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  521. ON CONFLICT ON CONSTRAINT my_table_idx_1 DO UPDATE SET data = %(param_1)s
  522. {stop}
  523. >>> do_update_stmt = insert_stmt.on_conflict_do_update(
  524. ... constraint="my_table_pk", set_=dict(data="updated value")
  525. ... )
  526. >>> print(do_update_stmt)
  527. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  528. ON CONFLICT ON CONSTRAINT my_table_pk DO UPDATE SET data = %(param_1)s
  529. {stop}
  530. * The :paramref:`_postgresql.Insert.on_conflict_do_update.constraint` argument may
  531. also refer to a SQLAlchemy construct representing a constraint,
  532. e.g. :class:`.UniqueConstraint`, :class:`.PrimaryKeyConstraint`,
  533. :class:`.Index`, or :class:`.ExcludeConstraint`. In this use,
  534. if the constraint has a name, it is used directly. Otherwise, if the
  535. constraint is unnamed, then inference will be used, where the expressions
  536. and optional WHERE clause of the constraint will be spelled out in the
  537. construct. This use is especially convenient
  538. to refer to the named or unnamed primary key of a :class:`_schema.Table`
  539. using the
  540. :attr:`_schema.Table.primary_key` attribute:
  541. .. sourcecode:: pycon+sql
  542. >>> do_update_stmt = insert_stmt.on_conflict_do_update(
  543. ... constraint=my_table.primary_key, set_=dict(data="updated value")
  544. ... )
  545. >>> print(do_update_stmt)
  546. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  547. ON CONFLICT (id) DO UPDATE SET data = %(param_1)s
  548. The SET Clause
  549. ^^^^^^^^^^^^^^^
  550. ``ON CONFLICT...DO UPDATE`` is used to perform an update of the already
  551. existing row, using any combination of new values as well as values
  552. from the proposed insertion. These values are specified using the
  553. :paramref:`_postgresql.Insert.on_conflict_do_update.set_` parameter. This
  554. parameter accepts a dictionary which consists of direct values
  555. for UPDATE:
  556. .. sourcecode:: pycon+sql
  557. >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
  558. >>> do_update_stmt = stmt.on_conflict_do_update(
  559. ... index_elements=["id"], set_=dict(data="updated value")
  560. ... )
  561. >>> print(do_update_stmt)
  562. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  563. ON CONFLICT (id) DO UPDATE SET data = %(param_1)s
  564. .. warning::
  565. The :meth:`_expression.Insert.on_conflict_do_update`
  566. method does **not** take into
  567. account Python-side default UPDATE values or generation functions, e.g.
  568. those specified using :paramref:`_schema.Column.onupdate`.
  569. These values will not be exercised for an ON CONFLICT style of UPDATE,
  570. unless they are manually specified in the
  571. :paramref:`_postgresql.Insert.on_conflict_do_update.set_` dictionary.
  572. Updating using the Excluded INSERT Values
  573. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  574. In order to refer to the proposed insertion row, the special alias
  575. :attr:`~.postgresql.Insert.excluded` is available as an attribute on
  576. the :class:`_postgresql.Insert` object; this object is a
  577. :class:`_expression.ColumnCollection`
  578. which alias contains all columns of the target
  579. table:
  580. .. sourcecode:: pycon+sql
  581. >>> stmt = insert(my_table).values(
  582. ... id="some_id", data="inserted value", author="jlh"
  583. ... )
  584. >>> do_update_stmt = stmt.on_conflict_do_update(
  585. ... index_elements=["id"],
  586. ... set_=dict(data="updated value", author=stmt.excluded.author),
  587. ... )
  588. >>> print(do_update_stmt)
  589. {printsql}INSERT INTO my_table (id, data, author)
  590. VALUES (%(id)s, %(data)s, %(author)s)
  591. ON CONFLICT (id) DO UPDATE SET data = %(param_1)s, author = excluded.author
  592. Additional WHERE Criteria
  593. ^^^^^^^^^^^^^^^^^^^^^^^^^
  594. The :meth:`_expression.Insert.on_conflict_do_update` method also accepts
  595. a WHERE clause using the :paramref:`_postgresql.Insert.on_conflict_do_update.where`
  596. parameter, which will limit those rows which receive an UPDATE:
  597. .. sourcecode:: pycon+sql
  598. >>> stmt = insert(my_table).values(
  599. ... id="some_id", data="inserted value", author="jlh"
  600. ... )
  601. >>> on_update_stmt = stmt.on_conflict_do_update(
  602. ... index_elements=["id"],
  603. ... set_=dict(data="updated value", author=stmt.excluded.author),
  604. ... where=(my_table.c.status == 2),
  605. ... )
  606. >>> print(on_update_stmt)
  607. {printsql}INSERT INTO my_table (id, data, author)
  608. VALUES (%(id)s, %(data)s, %(author)s)
  609. ON CONFLICT (id) DO UPDATE SET data = %(param_1)s, author = excluded.author
  610. WHERE my_table.status = %(status_1)s
  611. Skipping Rows with DO NOTHING
  612. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  613. ``ON CONFLICT`` may be used to skip inserting a row entirely
  614. if any conflict with a unique or exclusion constraint occurs; below
  615. this is illustrated using the
  616. :meth:`~.postgresql.Insert.on_conflict_do_nothing` method:
  617. .. sourcecode:: pycon+sql
  618. >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
  619. >>> stmt = stmt.on_conflict_do_nothing(index_elements=["id"])
  620. >>> print(stmt)
  621. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  622. ON CONFLICT (id) DO NOTHING
  623. If ``DO NOTHING`` is used without specifying any columns or constraint,
  624. it has the effect of skipping the INSERT for any unique or exclusion
  625. constraint violation which occurs:
  626. .. sourcecode:: pycon+sql
  627. >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
  628. >>> stmt = stmt.on_conflict_do_nothing()
  629. >>> print(stmt)
  630. {printsql}INSERT INTO my_table (id, data) VALUES (%(id)s, %(data)s)
  631. ON CONFLICT DO NOTHING
  632. .. _postgresql_match:
  633. Full Text Search
  634. ----------------
  635. PostgreSQL's full text search system is available through the use of the
  636. :data:`.func` namespace, combined with the use of custom operators
  637. via the :meth:`.Operators.bool_op` method. For simple cases with some
  638. degree of cross-backend compatibility, the :meth:`.Operators.match` operator
  639. may also be used.
  640. .. _postgresql_simple_match:
  641. Simple plain text matching with ``match()``
  642. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  643. The :meth:`.Operators.match` operator provides for cross-compatible simple
  644. text matching. For the PostgreSQL backend, it's hardcoded to generate
  645. an expression using the ``@@`` operator in conjunction with the
  646. ``plainto_tsquery()`` PostgreSQL function.
  647. On the PostgreSQL dialect, an expression like the following::
  648. select(sometable.c.text.match("search string"))
  649. would emit to the database:
  650. .. sourcecode:: sql
  651. SELECT text @@ plainto_tsquery('search string') FROM table
  652. Above, passing a plain string to :meth:`.Operators.match` will automatically
  653. make use of ``plainto_tsquery()`` to specify the type of tsquery. This
  654. establishes basic database cross-compatibility for :meth:`.Operators.match`
  655. with other backends.
  656. .. versionchanged:: 2.0 The default tsquery generation function used by the
  657. PostgreSQL dialect with :meth:`.Operators.match` is ``plainto_tsquery()``.
  658. To render exactly what was rendered in 1.4, use the following form::
  659. from sqlalchemy import func
  660. select(sometable.c.text.bool_op("@@")(func.to_tsquery("search string")))
  661. Which would emit:
  662. .. sourcecode:: sql
  663. SELECT text @@ to_tsquery('search string') FROM table
  664. Using PostgreSQL full text functions and operators directly
  665. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  666. Text search operations beyond the simple use of :meth:`.Operators.match`
  667. may make use of the :data:`.func` namespace to generate PostgreSQL full-text
  668. functions, in combination with :meth:`.Operators.bool_op` to generate
  669. any boolean operator.
  670. For example, the query::
  671. select(func.to_tsquery("cat").bool_op("@>")(func.to_tsquery("cat & rat")))
  672. would generate:
  673. .. sourcecode:: sql
  674. SELECT to_tsquery('cat') @> to_tsquery('cat & rat')
  675. The :class:`_postgresql.TSVECTOR` type can provide for explicit CAST::
  676. from sqlalchemy.dialects.postgresql import TSVECTOR
  677. from sqlalchemy import select, cast
  678. select(cast("some text", TSVECTOR))
  679. produces a statement equivalent to:
  680. .. sourcecode:: sql
  681. SELECT CAST('some text' AS TSVECTOR) AS anon_1
  682. The ``func`` namespace is augmented by the PostgreSQL dialect to set up
  683. correct argument and return types for most full text search functions.
  684. These functions are used automatically by the :attr:`_sql.func` namespace
  685. assuming the ``sqlalchemy.dialects.postgresql`` package has been imported,
  686. or :func:`_sa.create_engine` has been invoked using a ``postgresql``
  687. dialect. These functions are documented at:
  688. * :class:`_postgresql.to_tsvector`
  689. * :class:`_postgresql.to_tsquery`
  690. * :class:`_postgresql.plainto_tsquery`
  691. * :class:`_postgresql.phraseto_tsquery`
  692. * :class:`_postgresql.websearch_to_tsquery`
  693. * :class:`_postgresql.ts_headline`
  694. Specifying the "regconfig" with ``match()`` or custom operators
  695. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  696. PostgreSQL's ``plainto_tsquery()`` function accepts an optional
  697. "regconfig" argument that is used to instruct PostgreSQL to use a
  698. particular pre-computed GIN or GiST index in order to perform the search.
  699. When using :meth:`.Operators.match`, this additional parameter may be
  700. specified using the ``postgresql_regconfig`` parameter, such as::
  701. select(mytable.c.id).where(
  702. mytable.c.title.match("somestring", postgresql_regconfig="english")
  703. )
  704. Which would emit:
  705. .. sourcecode:: sql
  706. SELECT mytable.id FROM mytable
  707. WHERE mytable.title @@ plainto_tsquery('english', 'somestring')
  708. When using other PostgreSQL search functions with :data:`.func`, the
  709. "regconfig" parameter may be passed directly as the initial argument::
  710. select(mytable.c.id).where(
  711. func.to_tsvector("english", mytable.c.title).bool_op("@@")(
  712. func.to_tsquery("english", "somestring")
  713. )
  714. )
  715. produces a statement equivalent to:
  716. .. sourcecode:: sql
  717. SELECT mytable.id FROM mytable
  718. WHERE to_tsvector('english', mytable.title) @@
  719. to_tsquery('english', 'somestring')
  720. It is recommended that you use the ``EXPLAIN ANALYZE...`` tool from
  721. PostgreSQL to ensure that you are generating queries with SQLAlchemy that
  722. take full advantage of any indexes you may have created for full text search.
  723. .. seealso::
  724. `Full Text Search <https://www.postgresql.org/docs/current/textsearch-controls.html>`_ - in the PostgreSQL documentation
  725. FROM ONLY ...
  726. -------------
  727. The dialect supports PostgreSQL's ONLY keyword for targeting only a particular
  728. table in an inheritance hierarchy. This can be used to produce the
  729. ``SELECT ... FROM ONLY``, ``UPDATE ONLY ...``, and ``DELETE FROM ONLY ...``
  730. syntaxes. It uses SQLAlchemy's hints mechanism::
  731. # SELECT ... FROM ONLY ...
  732. result = table.select().with_hint(table, "ONLY", "postgresql")
  733. print(result.fetchall())
  734. # UPDATE ONLY ...
  735. table.update(values=dict(foo="bar")).with_hint(
  736. "ONLY", dialect_name="postgresql"
  737. )
  738. # DELETE FROM ONLY ...
  739. table.delete().with_hint("ONLY", dialect_name="postgresql")
  740. .. _postgresql_indexes:
  741. PostgreSQL-Specific Index Options
  742. ---------------------------------
  743. Several extensions to the :class:`.Index` construct are available, specific
  744. to the PostgreSQL dialect.
  745. .. _postgresql_covering_indexes:
  746. Covering Indexes
  747. ^^^^^^^^^^^^^^^^
  748. A covering index includes additional columns that are not part of the index key
  749. but are stored in the index, allowing PostgreSQL to satisfy queries using only
  750. the index without accessing the table (an "index-only scan"). This is
  751. indicated on the index using the ``INCLUDE`` clause. The
  752. ``postgresql_include`` option for :class:`.Index` (as well as
  753. :class:`.UniqueConstraint`) renders ``INCLUDE(colname)`` for the given string
  754. names::
  755. Index("my_index", table.c.x, postgresql_include=["y"])
  756. would render the index as ``CREATE INDEX my_index ON table (x) INCLUDE (y)``
  757. Note that this feature requires PostgreSQL 11 or later.
  758. .. seealso::
  759. :ref:`postgresql_constraint_options_include` - the same feature implemented
  760. for :class:`.UniqueConstraint`
  761. .. versionadded:: 1.4 - support for covering indexes with :class:`.Index`.
  762. support for :class:`.UniqueConstraint` was in 2.0.41
  763. .. _postgresql_partial_indexes:
  764. Partial Indexes
  765. ^^^^^^^^^^^^^^^
  766. Partial indexes add criterion to the index definition so that the index is
  767. applied to a subset of rows. These can be specified on :class:`.Index`
  768. using the ``postgresql_where`` keyword argument::
  769. Index("my_index", my_table.c.id, postgresql_where=my_table.c.value > 10)
  770. .. _postgresql_operator_classes:
  771. Operator Classes
  772. ^^^^^^^^^^^^^^^^
  773. PostgreSQL allows the specification of an *operator class* for each column of
  774. an index (see
  775. https://www.postgresql.org/docs/current/interactive/indexes-opclass.html).
  776. The :class:`.Index` construct allows these to be specified via the
  777. ``postgresql_ops`` keyword argument::
  778. Index(
  779. "my_index",
  780. my_table.c.id,
  781. my_table.c.data,
  782. postgresql_ops={"data": "text_pattern_ops", "id": "int4_ops"},
  783. )
  784. Note that the keys in the ``postgresql_ops`` dictionaries are the
  785. "key" name of the :class:`_schema.Column`, i.e. the name used to access it from
  786. the ``.c`` collection of :class:`_schema.Table`, which can be configured to be
  787. different than the actual name of the column as expressed in the database.
  788. If ``postgresql_ops`` is to be used against a complex SQL expression such
  789. as a function call, then to apply to the column it must be given a label
  790. that is identified in the dictionary by name, e.g.::
  791. Index(
  792. "my_index",
  793. my_table.c.id,
  794. func.lower(my_table.c.data).label("data_lower"),
  795. postgresql_ops={"data_lower": "text_pattern_ops", "id": "int4_ops"},
  796. )
  797. Operator classes are also supported by the
  798. :class:`_postgresql.ExcludeConstraint` construct using the
  799. :paramref:`_postgresql.ExcludeConstraint.ops` parameter. See that parameter for
  800. details.
  801. .. versionadded:: 1.3.21 added support for operator classes with
  802. :class:`_postgresql.ExcludeConstraint`.
  803. Index Types
  804. ^^^^^^^^^^^
  805. PostgreSQL provides several index types: B-Tree, Hash, GiST, and GIN, as well
  806. as the ability for users to create their own (see
  807. https://www.postgresql.org/docs/current/static/indexes-types.html). These can be
  808. specified on :class:`.Index` using the ``postgresql_using`` keyword argument::
  809. Index("my_index", my_table.c.data, postgresql_using="gin")
  810. The value passed to the keyword argument will be simply passed through to the
  811. underlying CREATE INDEX command, so it *must* be a valid index type for your
  812. version of PostgreSQL.
  813. .. _postgresql_index_storage:
  814. Index Storage Parameters
  815. ^^^^^^^^^^^^^^^^^^^^^^^^
  816. PostgreSQL allows storage parameters to be set on indexes. The storage
  817. parameters available depend on the index method used by the index. Storage
  818. parameters can be specified on :class:`.Index` using the ``postgresql_with``
  819. keyword argument::
  820. Index("my_index", my_table.c.data, postgresql_with={"fillfactor": 50})
  821. PostgreSQL allows to define the tablespace in which to create the index.
  822. The tablespace can be specified on :class:`.Index` using the
  823. ``postgresql_tablespace`` keyword argument::
  824. Index("my_index", my_table.c.data, postgresql_tablespace="my_tablespace")
  825. Note that the same option is available on :class:`_schema.Table` as well.
  826. .. _postgresql_index_concurrently:
  827. Indexes with CONCURRENTLY
  828. ^^^^^^^^^^^^^^^^^^^^^^^^^
  829. The PostgreSQL index option CONCURRENTLY is supported by passing the
  830. flag ``postgresql_concurrently`` to the :class:`.Index` construct::
  831. tbl = Table("testtbl", m, Column("data", Integer))
  832. idx1 = Index("test_idx1", tbl.c.data, postgresql_concurrently=True)
  833. The above index construct will render DDL for CREATE INDEX, assuming
  834. PostgreSQL 8.2 or higher is detected or for a connection-less dialect, as:
  835. .. sourcecode:: sql
  836. CREATE INDEX CONCURRENTLY test_idx1 ON testtbl (data)
  837. For DROP INDEX, assuming PostgreSQL 9.2 or higher is detected or for
  838. a connection-less dialect, it will emit:
  839. .. sourcecode:: sql
  840. DROP INDEX CONCURRENTLY test_idx1
  841. When using CONCURRENTLY, the PostgreSQL database requires that the statement
  842. be invoked outside of a transaction block. The Python DBAPI enforces that
  843. even for a single statement, a transaction is present, so to use this
  844. construct, the DBAPI's "autocommit" mode must be used::
  845. metadata = MetaData()
  846. table = Table("foo", metadata, Column("id", String))
  847. index = Index("foo_idx", table.c.id, postgresql_concurrently=True)
  848. with engine.connect() as conn:
  849. with conn.execution_options(isolation_level="AUTOCOMMIT"):
  850. table.create(conn)
  851. .. seealso::
  852. :ref:`postgresql_isolation_level`
  853. .. _postgresql_index_reflection:
  854. PostgreSQL Index Reflection
  855. ---------------------------
  856. The PostgreSQL database creates a UNIQUE INDEX implicitly whenever the
  857. UNIQUE CONSTRAINT construct is used. When inspecting a table using
  858. :class:`_reflection.Inspector`, the :meth:`_reflection.Inspector.get_indexes`
  859. and the :meth:`_reflection.Inspector.get_unique_constraints`
  860. will report on these
  861. two constructs distinctly; in the case of the index, the key
  862. ``duplicates_constraint`` will be present in the index entry if it is
  863. detected as mirroring a constraint. When performing reflection using
  864. ``Table(..., autoload_with=engine)``, the UNIQUE INDEX is **not** returned
  865. in :attr:`_schema.Table.indexes` when it is detected as mirroring a
  866. :class:`.UniqueConstraint` in the :attr:`_schema.Table.constraints` collection
  867. .
  868. Special Reflection Options
  869. --------------------------
  870. The :class:`_reflection.Inspector`
  871. used for the PostgreSQL backend is an instance
  872. of :class:`.PGInspector`, which offers additional methods::
  873. from sqlalchemy import create_engine, inspect
  874. engine = create_engine("postgresql+psycopg2://localhost/test")
  875. insp = inspect(engine) # will be a PGInspector
  876. print(insp.get_enums())
  877. .. autoclass:: PGInspector
  878. :members:
  879. .. _postgresql_table_options:
  880. PostgreSQL Table Options
  881. ------------------------
  882. Several options for CREATE TABLE are supported directly by the PostgreSQL
  883. dialect in conjunction with the :class:`_schema.Table` construct, listed in
  884. the following sections.
  885. .. seealso::
  886. `PostgreSQL CREATE TABLE options
  887. <https://www.postgresql.org/docs/current/static/sql-createtable.html>`_ -
  888. in the PostgreSQL documentation.
  889. ``INHERITS``
  890. ^^^^^^^^^^^^
  891. Specifies one or more parent tables from which this table inherits columns and
  892. constraints, enabling table inheritance hierarchies in PostgreSQL.
  893. ::
  894. Table("some_table", metadata, ..., postgresql_inherits="some_supertable")
  895. Table("some_table", metadata, ..., postgresql_inherits=("t1", "t2", ...))
  896. ``ON COMMIT``
  897. ^^^^^^^^^^^^^
  898. Controls the behavior of temporary tables at transaction commit, with options
  899. to preserve rows, delete rows, or drop the table.
  900. ::
  901. Table("some_table", metadata, ..., postgresql_on_commit="PRESERVE ROWS")
  902. ``PARTITION BY``
  903. ^^^^^^^^^^^^^^^^
  904. Declares the table as a partitioned table using the specified partitioning
  905. strategy (RANGE, LIST, or HASH) on the given column(s).
  906. ::
  907. Table(
  908. "some_table",
  909. metadata,
  910. ...,
  911. postgresql_partition_by="LIST (part_column)",
  912. )
  913. ``TABLESPACE``
  914. ^^^^^^^^^^^^^^
  915. Specifies the tablespace where the table will be stored, allowing control over
  916. the physical location of table data on disk.
  917. ::
  918. Table("some_table", metadata, ..., postgresql_tablespace="some_tablespace")
  919. The above option is also available on the :class:`.Index` construct.
  920. ``USING``
  921. ^^^^^^^^^
  922. Specifies the table access method to use for storing table data, such as
  923. ``heap`` (the default) or other custom access methods.
  924. ::
  925. Table("some_table", metadata, ..., postgresql_using="heap")
  926. .. versionadded:: 2.0.26
  927. ``WITH OIDS``
  928. ^^^^^^^^^^^^^
  929. Enables the legacy OID (object identifier) system column for the table, which
  930. assigns a unique identifier to each row.
  931. ::
  932. Table("some_table", metadata, ..., postgresql_with_oids=True)
  933. ``WITHOUT OIDS``
  934. ^^^^^^^^^^^^^^^^
  935. Explicitly disables the OID system column for the table (the default behavior
  936. in modern PostgreSQL versions).
  937. ::
  938. Table("some_table", metadata, ..., postgresql_with_oids=False)
  939. .. _postgresql_constraint_options:
  940. PostgreSQL Constraint Options
  941. -----------------------------
  942. The following sections indicate options which are supported by the PostgreSQL
  943. dialect in conjunction with selected constraint constructs.
  944. ``NOT VALID``
  945. ^^^^^^^^^^^^^
  946. Allows a constraint to be added without validating existing rows, improving
  947. performance when adding constraints to large tables. This option applies
  948. towards CHECK and FOREIGN KEY constraints when the constraint is being added
  949. to an existing table via ALTER TABLE, and has the effect that existing rows
  950. are not scanned during the ALTER operation against the constraint being added.
  951. When using a SQL migration tool such as `Alembic <https://alembic.sqlalchemy.org>`_
  952. that renders ALTER TABLE constructs, the ``postgresql_not_valid`` argument
  953. may be specified as an additional keyword argument within the operation
  954. that creates the constraint, as in the following Alembic example::
  955. def update():
  956. op.create_foreign_key(
  957. "fk_user_address",
  958. "address",
  959. "user",
  960. ["user_id"],
  961. ["id"],
  962. postgresql_not_valid=True,
  963. )
  964. The keyword is ultimately accepted directly by the
  965. :class:`_schema.CheckConstraint`, :class:`_schema.ForeignKeyConstraint`
  966. and :class:`_schema.ForeignKey` constructs; when using a tool like
  967. Alembic, dialect-specific keyword arguments are passed through to
  968. these constructs from the migration operation directives::
  969. CheckConstraint("some_field IS NOT NULL", postgresql_not_valid=True)
  970. ForeignKeyConstraint(
  971. ["some_id"], ["some_table.some_id"], postgresql_not_valid=True
  972. )
  973. .. versionadded:: 1.4.32
  974. .. seealso::
  975. `PostgreSQL ALTER TABLE options
  976. <https://www.postgresql.org/docs/current/static/sql-altertable.html>`_ -
  977. in the PostgreSQL documentation.
  978. .. _postgresql_constraint_options_include:
  979. ``INCLUDE``
  980. ^^^^^^^^^^^
  981. This keyword is applicable to both a ``UNIQUE`` constraint as well as an
  982. ``INDEX``. The ``postgresql_include`` option available for
  983. :class:`.UniqueConstraint` as well as :class:`.Index` creates a covering index
  984. by including additional columns in the underlying index without making them
  985. part of the key constraint. This option adds one or more columns as a "payload"
  986. to the index created automatically by PostgreSQL for the constraint. For
  987. example, the following table definition::
  988. Table(
  989. "mytable",
  990. metadata,
  991. Column("id", Integer, nullable=False),
  992. Column("value", Integer, nullable=False),
  993. UniqueConstraint("id", postgresql_include=["value"]),
  994. )
  995. would produce the DDL statement
  996. .. sourcecode:: sql
  997. CREATE TABLE mytable (
  998. id INTEGER NOT NULL,
  999. value INTEGER NOT NULL,
  1000. UNIQUE (id) INCLUDE (value)
  1001. )
  1002. Note that this feature requires PostgreSQL 11 or later.
  1003. .. versionadded:: 2.0.41 - added support for ``postgresql_include`` to
  1004. :class:`.UniqueConstraint`, to complement the existing feature in
  1005. :class:`.Index`.
  1006. .. seealso::
  1007. :ref:`postgresql_covering_indexes` - background on ``postgresql_include``
  1008. for the :class:`.Index` construct.
  1009. Column list with foreign key ``ON DELETE SET`` actions
  1010. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  1011. Allows selective column updates when a foreign key action is triggered, limiting
  1012. which columns are set to NULL or DEFAULT upon deletion of a referenced row.
  1013. This applies to :class:`.ForeignKey` and :class:`.ForeignKeyConstraint`, the
  1014. :paramref:`.ForeignKey.ondelete` parameter will accept on the PostgreSQL
  1015. backend only a string list of column names inside parenthesis, following the
  1016. ``SET NULL`` or ``SET DEFAULT`` phrases, which will limit the set of columns
  1017. that are subject to the action::
  1018. fktable = Table(
  1019. "fktable",
  1020. metadata,
  1021. Column("tid", Integer),
  1022. Column("id", Integer),
  1023. Column("fk_id_del_set_null", Integer),
  1024. ForeignKeyConstraint(
  1025. columns=["tid", "fk_id_del_set_null"],
  1026. refcolumns=[pktable.c.tid, pktable.c.id],
  1027. ondelete="SET NULL (fk_id_del_set_null)",
  1028. ),
  1029. )
  1030. .. versionadded:: 2.0.40
  1031. .. _postgresql_table_valued_overview:
  1032. Table values, Table and Column valued functions, Row and Tuple objects
  1033. -----------------------------------------------------------------------
  1034. PostgreSQL makes great use of modern SQL forms such as table-valued functions,
  1035. tables and rows as values. These constructs are commonly used as part
  1036. of PostgreSQL's support for complex datatypes such as JSON, ARRAY, and other
  1037. datatypes. SQLAlchemy's SQL expression language has native support for
  1038. most table-valued and row-valued forms.
  1039. .. _postgresql_table_valued:
  1040. Table-Valued Functions
  1041. ^^^^^^^^^^^^^^^^^^^^^^^
  1042. Many PostgreSQL built-in functions are intended to be used in the FROM clause
  1043. of a SELECT statement, and are capable of returning table rows or sets of table
  1044. rows. A large portion of PostgreSQL's JSON functions for example such as
  1045. ``json_array_elements()``, ``json_object_keys()``, ``json_each_text()``,
  1046. ``json_each()``, ``json_to_record()``, ``json_populate_recordset()`` use such
  1047. forms. These classes of SQL function calling forms in SQLAlchemy are available
  1048. using the :meth:`_functions.FunctionElement.table_valued` method in conjunction
  1049. with :class:`_functions.Function` objects generated from the :data:`_sql.func`
  1050. namespace.
  1051. Examples from PostgreSQL's reference documentation follow below:
  1052. * ``json_each()``:
  1053. .. sourcecode:: pycon+sql
  1054. >>> from sqlalchemy import select, func
  1055. >>> stmt = select(
  1056. ... func.json_each('{"a":"foo", "b":"bar"}').table_valued("key", "value")
  1057. ... )
  1058. >>> print(stmt)
  1059. {printsql}SELECT anon_1.key, anon_1.value
  1060. FROM json_each(:json_each_1) AS anon_1
  1061. * ``json_populate_record()``:
  1062. .. sourcecode:: pycon+sql
  1063. >>> from sqlalchemy import select, func, literal_column
  1064. >>> stmt = select(
  1065. ... func.json_populate_record(
  1066. ... literal_column("null::myrowtype"), '{"a":1,"b":2}'
  1067. ... ).table_valued("a", "b", name="x")
  1068. ... )
  1069. >>> print(stmt)
  1070. {printsql}SELECT x.a, x.b
  1071. FROM json_populate_record(null::myrowtype, :json_populate_record_1) AS x
  1072. * ``json_to_record()`` - this form uses a PostgreSQL specific form of derived
  1073. columns in the alias, where we may make use of :func:`_sql.column` elements with
  1074. types to produce them. The :meth:`_functions.FunctionElement.table_valued`
  1075. method produces a :class:`_sql.TableValuedAlias` construct, and the method
  1076. :meth:`_sql.TableValuedAlias.render_derived` method sets up the derived
  1077. columns specification:
  1078. .. sourcecode:: pycon+sql
  1079. >>> from sqlalchemy import select, func, column, Integer, Text
  1080. >>> stmt = select(
  1081. ... func.json_to_record('{"a":1,"b":[1,2,3],"c":"bar"}')
  1082. ... .table_valued(
  1083. ... column("a", Integer),
  1084. ... column("b", Text),
  1085. ... column("d", Text),
  1086. ... )
  1087. ... .render_derived(name="x", with_types=True)
  1088. ... )
  1089. >>> print(stmt)
  1090. {printsql}SELECT x.a, x.b, x.d
  1091. FROM json_to_record(:json_to_record_1) AS x(a INTEGER, b TEXT, d TEXT)
  1092. * ``WITH ORDINALITY`` - part of the SQL standard, ``WITH ORDINALITY`` adds an
  1093. ordinal counter to the output of a function and is accepted by a limited set
  1094. of PostgreSQL functions including ``unnest()`` and ``generate_series()``. The
  1095. :meth:`_functions.FunctionElement.table_valued` method accepts a keyword
  1096. parameter ``with_ordinality`` for this purpose, which accepts the string name
  1097. that will be applied to the "ordinality" column:
  1098. .. sourcecode:: pycon+sql
  1099. >>> from sqlalchemy import select, func
  1100. >>> stmt = select(
  1101. ... func.generate_series(4, 1, -1)
  1102. ... .table_valued("value", with_ordinality="ordinality")
  1103. ... .render_derived()
  1104. ... )
  1105. >>> print(stmt)
  1106. {printsql}SELECT anon_1.value, anon_1.ordinality
  1107. FROM generate_series(:generate_series_1, :generate_series_2, :generate_series_3)
  1108. WITH ORDINALITY AS anon_1(value, ordinality)
  1109. .. versionadded:: 1.4.0b2
  1110. .. seealso::
  1111. :ref:`tutorial_functions_table_valued` - in the :ref:`unified_tutorial`
  1112. .. _postgresql_column_valued:
  1113. Column Valued Functions
  1114. ^^^^^^^^^^^^^^^^^^^^^^^
  1115. Similar to the table valued function, a column valued function is present
  1116. in the FROM clause, but delivers itself to the columns clause as a single
  1117. scalar value. PostgreSQL functions such as ``json_array_elements()``,
  1118. ``unnest()`` and ``generate_series()`` may use this form. Column valued functions are available using the
  1119. :meth:`_functions.FunctionElement.column_valued` method of :class:`_functions.FunctionElement`:
  1120. * ``json_array_elements()``:
  1121. .. sourcecode:: pycon+sql
  1122. >>> from sqlalchemy import select, func
  1123. >>> stmt = select(
  1124. ... func.json_array_elements('["one", "two"]').column_valued("x")
  1125. ... )
  1126. >>> print(stmt)
  1127. {printsql}SELECT x
  1128. FROM json_array_elements(:json_array_elements_1) AS x
  1129. * ``unnest()`` - in order to generate a PostgreSQL ARRAY literal, the
  1130. :func:`_postgresql.array` construct may be used:
  1131. .. sourcecode:: pycon+sql
  1132. >>> from sqlalchemy.dialects.postgresql import array
  1133. >>> from sqlalchemy import select, func
  1134. >>> stmt = select(func.unnest(array([1, 2])).column_valued())
  1135. >>> print(stmt)
  1136. {printsql}SELECT anon_1
  1137. FROM unnest(ARRAY[%(param_1)s, %(param_2)s]) AS anon_1
  1138. The function can of course be used against an existing table-bound column
  1139. that's of type :class:`_types.ARRAY`:
  1140. .. sourcecode:: pycon+sql
  1141. >>> from sqlalchemy import table, column, ARRAY, Integer
  1142. >>> from sqlalchemy import select, func
  1143. >>> t = table("t", column("value", ARRAY(Integer)))
  1144. >>> stmt = select(func.unnest(t.c.value).column_valued("unnested_value"))
  1145. >>> print(stmt)
  1146. {printsql}SELECT unnested_value
  1147. FROM unnest(t.value) AS unnested_value
  1148. .. seealso::
  1149. :ref:`tutorial_functions_column_valued` - in the :ref:`unified_tutorial`
  1150. Row Types
  1151. ^^^^^^^^^
  1152. Built-in support for rendering a ``ROW`` may be approximated using
  1153. ``func.ROW`` with the :attr:`_sa.func` namespace, or by using the
  1154. :func:`_sql.tuple_` construct:
  1155. .. sourcecode:: pycon+sql
  1156. >>> from sqlalchemy import table, column, func, tuple_
  1157. >>> t = table("t", column("id"), column("fk"))
  1158. >>> stmt = (
  1159. ... t.select()
  1160. ... .where(tuple_(t.c.id, t.c.fk) > (1, 2))
  1161. ... .where(func.ROW(t.c.id, t.c.fk) < func.ROW(3, 7))
  1162. ... )
  1163. >>> print(stmt)
  1164. {printsql}SELECT t.id, t.fk
  1165. FROM t
  1166. WHERE (t.id, t.fk) > (:param_1, :param_2) AND ROW(t.id, t.fk) < ROW(:ROW_1, :ROW_2)
  1167. .. seealso::
  1168. `PostgreSQL Row Constructors
  1169. <https://www.postgresql.org/docs/current/sql-expressions.html#SQL-SYNTAX-ROW-CONSTRUCTORS>`_
  1170. `PostgreSQL Row Constructor Comparison
  1171. <https://www.postgresql.org/docs/current/functions-comparisons.html#ROW-WISE-COMPARISON>`_
  1172. Table Types passed to Functions
  1173. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  1174. PostgreSQL supports passing a table as an argument to a function, which is
  1175. known as a "record" type. SQLAlchemy :class:`_sql.FromClause` objects
  1176. such as :class:`_schema.Table` support this special form using the
  1177. :meth:`_sql.FromClause.table_valued` method, which is comparable to the
  1178. :meth:`_functions.FunctionElement.table_valued` method except that the collection
  1179. of columns is already established by that of the :class:`_sql.FromClause`
  1180. itself:
  1181. .. sourcecode:: pycon+sql
  1182. >>> from sqlalchemy import table, column, func, select
  1183. >>> a = table("a", column("id"), column("x"), column("y"))
  1184. >>> stmt = select(func.row_to_json(a.table_valued()))
  1185. >>> print(stmt)
  1186. {printsql}SELECT row_to_json(a) AS row_to_json_1
  1187. FROM a
  1188. .. versionadded:: 1.4.0b2
  1189. """ # noqa: E501
  1190. from __future__ import annotations
  1191. from collections import defaultdict
  1192. from functools import lru_cache
  1193. import re
  1194. from typing import Any
  1195. from typing import cast
  1196. from typing import Dict
  1197. from typing import List
  1198. from typing import Optional
  1199. from typing import Tuple
  1200. from typing import TYPE_CHECKING
  1201. from typing import Union
  1202. from . import arraylib as _array
  1203. from . import json as _json
  1204. from . import pg_catalog
  1205. from . import ranges as _ranges
  1206. from .ext import _regconfig_fn
  1207. from .ext import aggregate_order_by
  1208. from .hstore import HSTORE
  1209. from .named_types import CreateDomainType as CreateDomainType # noqa: F401
  1210. from .named_types import CreateEnumType as CreateEnumType # noqa: F401
  1211. from .named_types import DOMAIN as DOMAIN # noqa: F401
  1212. from .named_types import DropDomainType as DropDomainType # noqa: F401
  1213. from .named_types import DropEnumType as DropEnumType # noqa: F401
  1214. from .named_types import ENUM as ENUM # noqa: F401
  1215. from .named_types import NamedType as NamedType # noqa: F401
  1216. from .types import _DECIMAL_TYPES # noqa: F401
  1217. from .types import _FLOAT_TYPES # noqa: F401
  1218. from .types import _INT_TYPES # noqa: F401
  1219. from .types import BIT as BIT
  1220. from .types import BYTEA as BYTEA
  1221. from .types import CIDR as CIDR
  1222. from .types import CITEXT as CITEXT
  1223. from .types import INET as INET
  1224. from .types import INTERVAL as INTERVAL
  1225. from .types import MACADDR as MACADDR
  1226. from .types import MACADDR8 as MACADDR8
  1227. from .types import MONEY as MONEY
  1228. from .types import OID as OID
  1229. from .types import PGBit as PGBit # noqa: F401
  1230. from .types import PGCidr as PGCidr # noqa: F401
  1231. from .types import PGInet as PGInet # noqa: F401
  1232. from .types import PGInterval as PGInterval # noqa: F401
  1233. from .types import PGMacAddr as PGMacAddr # noqa: F401
  1234. from .types import PGMacAddr8 as PGMacAddr8 # noqa: F401
  1235. from .types import PGUuid as PGUuid
  1236. from .types import REGCLASS as REGCLASS
  1237. from .types import REGCONFIG as REGCONFIG # noqa: F401
  1238. from .types import TIME as TIME
  1239. from .types import TIMESTAMP as TIMESTAMP
  1240. from .types import TSVECTOR as TSVECTOR
  1241. from ... import exc
  1242. from ... import schema
  1243. from ... import select
  1244. from ... import sql
  1245. from ... import util
  1246. from ...engine import characteristics
  1247. from ...engine import default
  1248. from ...engine import interfaces
  1249. from ...engine import ObjectKind
  1250. from ...engine import ObjectScope
  1251. from ...engine import reflection
  1252. from ...engine import URL
  1253. from ...engine.reflection import ReflectionDefaults
  1254. from ...sql import bindparam
  1255. from ...sql import coercions
  1256. from ...sql import compiler
  1257. from ...sql import elements
  1258. from ...sql import expression
  1259. from ...sql import functions
  1260. from ...sql import roles
  1261. from ...sql import sqltypes
  1262. from ...sql import util as sql_util
  1263. from ...sql.compiler import InsertmanyvaluesSentinelOpts
  1264. from ...sql.visitors import InternalTraversal
  1265. from ...types import BIGINT
  1266. from ...types import BOOLEAN
  1267. from ...types import CHAR
  1268. from ...types import DATE
  1269. from ...types import DOUBLE_PRECISION
  1270. from ...types import FLOAT
  1271. from ...types import INTEGER
  1272. from ...types import NUMERIC
  1273. from ...types import REAL
  1274. from ...types import SMALLINT
  1275. from ...types import TEXT
  1276. from ...types import UUID as UUID
  1277. from ...types import VARCHAR
  1278. from ...util.typing import TypedDict
  1279. IDX_USING = re.compile(r"^(?:btree|hash|gist|gin|[\w_]+)$", re.I)
  1280. RESERVED_WORDS = {
  1281. "all",
  1282. "analyse",
  1283. "analyze",
  1284. "and",
  1285. "any",
  1286. "array",
  1287. "as",
  1288. "asc",
  1289. "asymmetric",
  1290. "both",
  1291. "case",
  1292. "cast",
  1293. "check",
  1294. "collate",
  1295. "column",
  1296. "constraint",
  1297. "create",
  1298. "current_catalog",
  1299. "current_date",
  1300. "current_role",
  1301. "current_time",
  1302. "current_timestamp",
  1303. "current_user",
  1304. "default",
  1305. "deferrable",
  1306. "desc",
  1307. "distinct",
  1308. "do",
  1309. "else",
  1310. "end",
  1311. "except",
  1312. "false",
  1313. "fetch",
  1314. "for",
  1315. "foreign",
  1316. "from",
  1317. "grant",
  1318. "group",
  1319. "having",
  1320. "in",
  1321. "initially",
  1322. "intersect",
  1323. "into",
  1324. "leading",
  1325. "limit",
  1326. "localtime",
  1327. "localtimestamp",
  1328. "new",
  1329. "not",
  1330. "null",
  1331. "of",
  1332. "off",
  1333. "offset",
  1334. "old",
  1335. "on",
  1336. "only",
  1337. "or",
  1338. "order",
  1339. "placing",
  1340. "primary",
  1341. "references",
  1342. "returning",
  1343. "select",
  1344. "session_user",
  1345. "some",
  1346. "symmetric",
  1347. "table",
  1348. "then",
  1349. "to",
  1350. "trailing",
  1351. "true",
  1352. "union",
  1353. "unique",
  1354. "user",
  1355. "using",
  1356. "variadic",
  1357. "when",
  1358. "where",
  1359. "window",
  1360. "with",
  1361. "authorization",
  1362. "between",
  1363. "binary",
  1364. "cross",
  1365. "current_schema",
  1366. "freeze",
  1367. "full",
  1368. "ilike",
  1369. "inner",
  1370. "is",
  1371. "isnull",
  1372. "join",
  1373. "left",
  1374. "like",
  1375. "natural",
  1376. "notnull",
  1377. "outer",
  1378. "over",
  1379. "overlaps",
  1380. "right",
  1381. "similar",
  1382. "verbose",
  1383. }
  1384. colspecs = {
  1385. sqltypes.ARRAY: _array.ARRAY,
  1386. sqltypes.Interval: INTERVAL,
  1387. sqltypes.Enum: ENUM,
  1388. sqltypes.JSON.JSONPathType: _json.JSONPATH,
  1389. sqltypes.JSON: _json.JSON,
  1390. sqltypes.Uuid: PGUuid,
  1391. }
  1392. ischema_names = {
  1393. "_array": _array.ARRAY,
  1394. "hstore": HSTORE,
  1395. "json": _json.JSON,
  1396. "jsonb": _json.JSONB,
  1397. "int4range": _ranges.INT4RANGE,
  1398. "int8range": _ranges.INT8RANGE,
  1399. "numrange": _ranges.NUMRANGE,
  1400. "daterange": _ranges.DATERANGE,
  1401. "tsrange": _ranges.TSRANGE,
  1402. "tstzrange": _ranges.TSTZRANGE,
  1403. "int4multirange": _ranges.INT4MULTIRANGE,
  1404. "int8multirange": _ranges.INT8MULTIRANGE,
  1405. "nummultirange": _ranges.NUMMULTIRANGE,
  1406. "datemultirange": _ranges.DATEMULTIRANGE,
  1407. "tsmultirange": _ranges.TSMULTIRANGE,
  1408. "tstzmultirange": _ranges.TSTZMULTIRANGE,
  1409. "integer": INTEGER,
  1410. "bigint": BIGINT,
  1411. "smallint": SMALLINT,
  1412. "character varying": VARCHAR,
  1413. "character": CHAR,
  1414. '"char"': sqltypes.String,
  1415. "name": sqltypes.String,
  1416. "text": TEXT,
  1417. "numeric": NUMERIC,
  1418. "float": FLOAT,
  1419. "real": REAL,
  1420. "inet": INET,
  1421. "cidr": CIDR,
  1422. "citext": CITEXT,
  1423. "uuid": UUID,
  1424. "bit": BIT,
  1425. "bit varying": BIT,
  1426. "macaddr": MACADDR,
  1427. "macaddr8": MACADDR8,
  1428. "money": MONEY,
  1429. "oid": OID,
  1430. "regclass": REGCLASS,
  1431. "double precision": DOUBLE_PRECISION,
  1432. "timestamp": TIMESTAMP,
  1433. "timestamp with time zone": TIMESTAMP,
  1434. "timestamp without time zone": TIMESTAMP,
  1435. "time with time zone": TIME,
  1436. "time without time zone": TIME,
  1437. "date": DATE,
  1438. "time": TIME,
  1439. "bytea": BYTEA,
  1440. "boolean": BOOLEAN,
  1441. "interval": INTERVAL,
  1442. "tsvector": TSVECTOR,
  1443. }
  1444. class PGCompiler(compiler.SQLCompiler):
  1445. def visit_to_tsvector_func(self, element, **kw):
  1446. return self._assert_pg_ts_ext(element, **kw)
  1447. def visit_to_tsquery_func(self, element, **kw):
  1448. return self._assert_pg_ts_ext(element, **kw)
  1449. def visit_plainto_tsquery_func(self, element, **kw):
  1450. return self._assert_pg_ts_ext(element, **kw)
  1451. def visit_phraseto_tsquery_func(self, element, **kw):
  1452. return self._assert_pg_ts_ext(element, **kw)
  1453. def visit_websearch_to_tsquery_func(self, element, **kw):
  1454. return self._assert_pg_ts_ext(element, **kw)
  1455. def visit_ts_headline_func(self, element, **kw):
  1456. return self._assert_pg_ts_ext(element, **kw)
  1457. def _assert_pg_ts_ext(self, element, **kw):
  1458. if not isinstance(element, _regconfig_fn):
  1459. # other options here include trying to rewrite the function
  1460. # with the correct types. however, that means we have to
  1461. # "un-SQL-ize" the first argument, which can't work in a
  1462. # generalized way. Also, parent compiler class has already added
  1463. # the incorrect return type to the result map. So let's just
  1464. # make sure the function we want is used up front.
  1465. raise exc.CompileError(
  1466. f'Can\'t compile "{element.name}()" full text search '
  1467. f"function construct that does not originate from the "
  1468. f'"sqlalchemy.dialects.postgresql" package. '
  1469. f'Please ensure "import sqlalchemy.dialects.postgresql" is '
  1470. f"called before constructing "
  1471. f'"sqlalchemy.func.{element.name}()" to ensure registration '
  1472. f"of the correct argument and return types."
  1473. )
  1474. return f"{element.name}{self.function_argspec(element, **kw)}"
  1475. def render_bind_cast(self, type_, dbapi_type, sqltext):
  1476. if dbapi_type._type_affinity is sqltypes.String and dbapi_type.length:
  1477. # use VARCHAR with no length for VARCHAR cast.
  1478. # see #9511
  1479. dbapi_type = sqltypes.STRINGTYPE
  1480. return f"""{sqltext}::{
  1481. self.dialect.type_compiler_instance.process(
  1482. dbapi_type, identifier_preparer=self.preparer
  1483. )
  1484. }"""
  1485. def visit_array(self, element, **kw):
  1486. if not element.clauses and not element.type.item_type._isnull:
  1487. return "ARRAY[]::%s" % element.type.compile(self.dialect)
  1488. return "ARRAY[%s]" % self.visit_clauselist(element, **kw)
  1489. def visit_slice(self, element, **kw):
  1490. return "%s:%s" % (
  1491. self.process(element.start, **kw),
  1492. self.process(element.stop, **kw),
  1493. )
  1494. def visit_bitwise_xor_op_binary(self, binary, operator, **kw):
  1495. return self._generate_generic_binary(binary, " # ", **kw)
  1496. def visit_json_getitem_op_binary(
  1497. self, binary, operator, _cast_applied=False, **kw
  1498. ):
  1499. if (
  1500. not _cast_applied
  1501. and binary.type._type_affinity is not sqltypes.JSON
  1502. ):
  1503. kw["_cast_applied"] = True
  1504. return self.process(sql.cast(binary, binary.type), **kw)
  1505. kw["eager_grouping"] = True
  1506. if (
  1507. not _cast_applied
  1508. and isinstance(binary.left.type, _json.JSONB)
  1509. and self.dialect._supports_jsonb_subscripting
  1510. ):
  1511. left = binary.left
  1512. if isinstance(left, (functions.FunctionElement, elements.Cast)):
  1513. left = elements.Grouping(left)
  1514. # for pg14+JSONB use subscript notation: col['key'] instead
  1515. # of col -> 'key'
  1516. return "%s[%s]" % (
  1517. self.process(left, **kw),
  1518. self.process(binary.right, **kw),
  1519. )
  1520. else:
  1521. # Fall back to arrow notation for older versions or when cast
  1522. # is applied
  1523. return self._generate_generic_binary(
  1524. binary, " -> " if not _cast_applied else " ->> ", **kw
  1525. )
  1526. def visit_json_path_getitem_op_binary(
  1527. self, binary, operator, _cast_applied=False, **kw
  1528. ):
  1529. if (
  1530. not _cast_applied
  1531. and binary.type._type_affinity is not sqltypes.JSON
  1532. ):
  1533. kw["_cast_applied"] = True
  1534. return self.process(sql.cast(binary, binary.type), **kw)
  1535. kw["eager_grouping"] = True
  1536. return self._generate_generic_binary(
  1537. binary, " #> " if not _cast_applied else " #>> ", **kw
  1538. )
  1539. def visit_getitem_binary(self, binary, operator, **kw):
  1540. return "%s[%s]" % (
  1541. self.process(binary.left, **kw),
  1542. self.process(binary.right, **kw),
  1543. )
  1544. def visit_aggregate_order_by(self, element, **kw):
  1545. return "%s ORDER BY %s" % (
  1546. self.process(element.target, **kw),
  1547. self.process(element.order_by, **kw),
  1548. )
  1549. def visit_match_op_binary(self, binary, operator, **kw):
  1550. if "postgresql_regconfig" in binary.modifiers:
  1551. regconfig = self.render_literal_value(
  1552. binary.modifiers["postgresql_regconfig"], sqltypes.STRINGTYPE
  1553. )
  1554. if regconfig:
  1555. return "%s @@ plainto_tsquery(%s, %s)" % (
  1556. self.process(binary.left, **kw),
  1557. regconfig,
  1558. self.process(binary.right, **kw),
  1559. )
  1560. return "%s @@ plainto_tsquery(%s)" % (
  1561. self.process(binary.left, **kw),
  1562. self.process(binary.right, **kw),
  1563. )
  1564. def visit_ilike_case_insensitive_operand(self, element, **kw):
  1565. return element.element._compiler_dispatch(self, **kw)
  1566. def visit_ilike_op_binary(self, binary, operator, **kw):
  1567. escape = binary.modifiers.get("escape", None)
  1568. return "%s ILIKE %s" % (
  1569. self.process(binary.left, **kw),
  1570. self.process(binary.right, **kw),
  1571. ) + (
  1572. " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE)
  1573. if escape is not None
  1574. else ""
  1575. )
  1576. def visit_not_ilike_op_binary(self, binary, operator, **kw):
  1577. escape = binary.modifiers.get("escape", None)
  1578. return "%s NOT ILIKE %s" % (
  1579. self.process(binary.left, **kw),
  1580. self.process(binary.right, **kw),
  1581. ) + (
  1582. " ESCAPE " + self.render_literal_value(escape, sqltypes.STRINGTYPE)
  1583. if escape is not None
  1584. else ""
  1585. )
  1586. def _regexp_match(self, base_op, binary, operator, kw):
  1587. flags = binary.modifiers["flags"]
  1588. if flags is None:
  1589. return self._generate_generic_binary(
  1590. binary, " %s " % base_op, **kw
  1591. )
  1592. if flags == "i":
  1593. return self._generate_generic_binary(
  1594. binary, " %s* " % base_op, **kw
  1595. )
  1596. return "%s %s CONCAT('(?', %s, ')', %s)" % (
  1597. self.process(binary.left, **kw),
  1598. base_op,
  1599. self.render_literal_value(flags, sqltypes.STRINGTYPE),
  1600. self.process(binary.right, **kw),
  1601. )
  1602. def visit_regexp_match_op_binary(self, binary, operator, **kw):
  1603. return self._regexp_match("~", binary, operator, kw)
  1604. def visit_not_regexp_match_op_binary(self, binary, operator, **kw):
  1605. return self._regexp_match("!~", binary, operator, kw)
  1606. def visit_regexp_replace_op_binary(self, binary, operator, **kw):
  1607. string = self.process(binary.left, **kw)
  1608. pattern_replace = self.process(binary.right, **kw)
  1609. flags = binary.modifiers["flags"]
  1610. if flags is None:
  1611. return "REGEXP_REPLACE(%s, %s)" % (
  1612. string,
  1613. pattern_replace,
  1614. )
  1615. else:
  1616. return "REGEXP_REPLACE(%s, %s, %s)" % (
  1617. string,
  1618. pattern_replace,
  1619. self.render_literal_value(flags, sqltypes.STRINGTYPE),
  1620. )
  1621. def visit_empty_set_expr(self, element_types, **kw):
  1622. # cast the empty set to the type we are comparing against. if
  1623. # we are comparing against the null type, pick an arbitrary
  1624. # datatype for the empty set
  1625. return "SELECT %s WHERE 1!=1" % (
  1626. ", ".join(
  1627. "CAST(NULL AS %s)"
  1628. % self.dialect.type_compiler_instance.process(
  1629. INTEGER() if type_._isnull else type_
  1630. )
  1631. for type_ in element_types or [INTEGER()]
  1632. ),
  1633. )
  1634. def render_literal_value(self, value, type_):
  1635. value = super().render_literal_value(value, type_)
  1636. if self.dialect._backslash_escapes:
  1637. value = value.replace("\\", "\\\\")
  1638. return value
  1639. def visit_aggregate_strings_func(self, fn, **kw):
  1640. return "string_agg%s" % self.function_argspec(fn)
  1641. def visit_sequence(self, seq, **kw):
  1642. return "nextval('%s')" % self.preparer.format_sequence(seq)
  1643. def limit_clause(self, select, **kw):
  1644. text = ""
  1645. if select._limit_clause is not None:
  1646. text += " \n LIMIT " + self.process(select._limit_clause, **kw)
  1647. if select._offset_clause is not None:
  1648. if select._limit_clause is None:
  1649. text += "\n LIMIT ALL"
  1650. text += " OFFSET " + self.process(select._offset_clause, **kw)
  1651. return text
  1652. def format_from_hint_text(self, sqltext, table, hint, iscrud):
  1653. if hint.upper() != "ONLY":
  1654. raise exc.CompileError("Unrecognized hint: %r" % hint)
  1655. return "ONLY " + sqltext
  1656. def get_select_precolumns(self, select, **kw):
  1657. # Do not call super().get_select_precolumns because
  1658. # it will warn/raise when distinct on is present
  1659. if select._distinct or select._distinct_on:
  1660. if select._distinct_on:
  1661. return (
  1662. "DISTINCT ON ("
  1663. + ", ".join(
  1664. [
  1665. self.process(col, **kw)
  1666. for col in select._distinct_on
  1667. ]
  1668. )
  1669. + ") "
  1670. )
  1671. else:
  1672. return "DISTINCT "
  1673. else:
  1674. return ""
  1675. def for_update_clause(self, select, **kw):
  1676. if select._for_update_arg.read:
  1677. if select._for_update_arg.key_share:
  1678. tmp = " FOR KEY SHARE"
  1679. else:
  1680. tmp = " FOR SHARE"
  1681. elif select._for_update_arg.key_share:
  1682. tmp = " FOR NO KEY UPDATE"
  1683. else:
  1684. tmp = " FOR UPDATE"
  1685. if select._for_update_arg.of:
  1686. tables = util.OrderedSet()
  1687. for c in select._for_update_arg.of:
  1688. tables.update(sql_util.surface_selectables_only(c))
  1689. of_kw = dict(kw)
  1690. of_kw.update(ashint=True, use_schema=False)
  1691. tmp += " OF " + ", ".join(
  1692. self.process(table, **of_kw) for table in tables
  1693. )
  1694. if select._for_update_arg.nowait:
  1695. tmp += " NOWAIT"
  1696. if select._for_update_arg.skip_locked:
  1697. tmp += " SKIP LOCKED"
  1698. return tmp
  1699. def visit_substring_func(self, func, **kw):
  1700. s = self.process(func.clauses.clauses[0], **kw)
  1701. start = self.process(func.clauses.clauses[1], **kw)
  1702. if len(func.clauses.clauses) > 2:
  1703. length = self.process(func.clauses.clauses[2], **kw)
  1704. return "SUBSTRING(%s FROM %s FOR %s)" % (s, start, length)
  1705. else:
  1706. return "SUBSTRING(%s FROM %s)" % (s, start)
  1707. def _on_conflict_target(self, clause, **kw):
  1708. if clause.constraint_target is not None:
  1709. # target may be a name of an Index, UniqueConstraint or
  1710. # ExcludeConstraint. While there is a separate
  1711. # "max_identifier_length" for indexes, PostgreSQL uses the same
  1712. # length for all objects so we can use
  1713. # truncate_and_render_constraint_name
  1714. target_text = (
  1715. "ON CONSTRAINT %s"
  1716. % self.preparer.truncate_and_render_constraint_name(
  1717. clause.constraint_target
  1718. )
  1719. )
  1720. elif clause.inferred_target_elements is not None:
  1721. target_text = "(%s)" % ", ".join(
  1722. (
  1723. self.preparer.quote(c)
  1724. if isinstance(c, str)
  1725. else self.process(c, include_table=False, use_schema=False)
  1726. )
  1727. for c in clause.inferred_target_elements
  1728. )
  1729. if clause.inferred_target_whereclause is not None:
  1730. whereclause_kw = dict(kw)
  1731. whereclause_kw.update(include_table=False, use_schema=False)
  1732. target_text += " WHERE %s" % self.process(
  1733. clause.inferred_target_whereclause,
  1734. **whereclause_kw,
  1735. )
  1736. else:
  1737. target_text = ""
  1738. return target_text
  1739. def visit_on_conflict_do_nothing(self, on_conflict, **kw):
  1740. target_text = self._on_conflict_target(on_conflict, **kw)
  1741. if target_text:
  1742. return "ON CONFLICT %s DO NOTHING" % target_text
  1743. else:
  1744. return "ON CONFLICT DO NOTHING"
  1745. def visit_on_conflict_do_update(self, on_conflict, **kw):
  1746. clause = on_conflict
  1747. target_text = self._on_conflict_target(on_conflict, **kw)
  1748. action_set_ops = []
  1749. set_parameters = dict(clause.update_values_to_set)
  1750. # create a list of column assignment clauses as tuples
  1751. insert_statement = self.stack[-1]["selectable"]
  1752. cols = insert_statement.table.c
  1753. set_kw = dict(kw)
  1754. set_kw.update(use_schema=False)
  1755. for c in cols:
  1756. col_key = c.key
  1757. if col_key in set_parameters:
  1758. value = set_parameters.pop(col_key)
  1759. elif c in set_parameters:
  1760. value = set_parameters.pop(c)
  1761. else:
  1762. continue
  1763. # TODO: this coercion should be up front. we can't cache
  1764. # SQL constructs with non-bound literals buried in them
  1765. if coercions._is_literal(value):
  1766. value = elements.BindParameter(None, value, type_=c.type)
  1767. else:
  1768. if (
  1769. isinstance(value, elements.BindParameter)
  1770. and value.type._isnull
  1771. ):
  1772. value = value._clone()
  1773. value.type = c.type
  1774. value_text = self.process(
  1775. value.self_group(), is_upsert_set=True, **set_kw
  1776. )
  1777. key_text = self.preparer.quote(c.name)
  1778. action_set_ops.append("%s = %s" % (key_text, value_text))
  1779. # check for names that don't match columns
  1780. if set_parameters:
  1781. util.warn(
  1782. "Additional column names not matching "
  1783. "any column keys in table '%s': %s"
  1784. % (
  1785. self.current_executable.table.name,
  1786. (", ".join("'%s'" % c for c in set_parameters)),
  1787. )
  1788. )
  1789. for k, v in set_parameters.items():
  1790. key_text = (
  1791. self.preparer.quote(k)
  1792. if isinstance(k, str)
  1793. else self.process(k, use_schema=False)
  1794. )
  1795. value_text = self.process(
  1796. coercions.expect(roles.ExpressionElementRole, v),
  1797. is_upsert_set=True,
  1798. **set_kw,
  1799. )
  1800. action_set_ops.append("%s = %s" % (key_text, value_text))
  1801. action_text = ", ".join(action_set_ops)
  1802. if clause.update_whereclause is not None:
  1803. where_kw = dict(kw)
  1804. where_kw.update(include_table=True, use_schema=False)
  1805. action_text += " WHERE %s" % self.process(
  1806. clause.update_whereclause, **where_kw
  1807. )
  1808. return "ON CONFLICT %s DO UPDATE SET %s" % (target_text, action_text)
  1809. def update_from_clause(
  1810. self, update_stmt, from_table, extra_froms, from_hints, **kw
  1811. ):
  1812. kw["asfrom"] = True
  1813. return "FROM " + ", ".join(
  1814. t._compiler_dispatch(self, fromhints=from_hints, **kw)
  1815. for t in extra_froms
  1816. )
  1817. def delete_extra_from_clause(
  1818. self, delete_stmt, from_table, extra_froms, from_hints, **kw
  1819. ):
  1820. """Render the DELETE .. USING clause specific to PostgreSQL."""
  1821. kw["asfrom"] = True
  1822. return "USING " + ", ".join(
  1823. t._compiler_dispatch(self, fromhints=from_hints, **kw)
  1824. for t in extra_froms
  1825. )
  1826. def fetch_clause(self, select, **kw):
  1827. # pg requires parens for non literal clauses. It's also required for
  1828. # bind parameters if a ::type casts is used by the driver (asyncpg),
  1829. # so it's easiest to just always add it
  1830. text = ""
  1831. if select._offset_clause is not None:
  1832. text += "\n OFFSET (%s) ROWS" % self.process(
  1833. select._offset_clause, **kw
  1834. )
  1835. if select._fetch_clause is not None:
  1836. text += "\n FETCH FIRST (%s)%s ROWS %s" % (
  1837. self.process(select._fetch_clause, **kw),
  1838. " PERCENT" if select._fetch_clause_options["percent"] else "",
  1839. (
  1840. "WITH TIES"
  1841. if select._fetch_clause_options["with_ties"]
  1842. else "ONLY"
  1843. ),
  1844. )
  1845. return text
  1846. class PGDDLCompiler(compiler.DDLCompiler):
  1847. def get_column_specification(self, column, **kwargs):
  1848. colspec = self.preparer.format_column(column)
  1849. impl_type = column.type.dialect_impl(self.dialect)
  1850. if isinstance(impl_type, sqltypes.TypeDecorator):
  1851. impl_type = impl_type.impl
  1852. has_identity = (
  1853. column.identity is not None
  1854. and self.dialect.supports_identity_columns
  1855. )
  1856. if (
  1857. column.primary_key
  1858. and column is column.table._autoincrement_column
  1859. and (
  1860. self.dialect.supports_smallserial
  1861. or not isinstance(impl_type, sqltypes.SmallInteger)
  1862. )
  1863. and not has_identity
  1864. and (
  1865. column.default is None
  1866. or (
  1867. isinstance(column.default, schema.Sequence)
  1868. and column.default.optional
  1869. )
  1870. )
  1871. ):
  1872. if isinstance(impl_type, sqltypes.BigInteger):
  1873. colspec += " BIGSERIAL"
  1874. elif isinstance(impl_type, sqltypes.SmallInteger):
  1875. colspec += " SMALLSERIAL"
  1876. else:
  1877. colspec += " SERIAL"
  1878. else:
  1879. colspec += " " + self.dialect.type_compiler_instance.process(
  1880. column.type,
  1881. type_expression=column,
  1882. identifier_preparer=self.preparer,
  1883. )
  1884. default = self.get_column_default_string(column)
  1885. if default is not None:
  1886. colspec += " DEFAULT " + default
  1887. if column.computed is not None:
  1888. colspec += " " + self.process(column.computed)
  1889. if has_identity:
  1890. colspec += " " + self.process(column.identity)
  1891. if not column.nullable and not has_identity:
  1892. colspec += " NOT NULL"
  1893. elif column.nullable and has_identity:
  1894. colspec += " NULL"
  1895. return colspec
  1896. def _define_constraint_validity(self, constraint):
  1897. not_valid = constraint.dialect_options["postgresql"]["not_valid"]
  1898. return " NOT VALID" if not_valid else ""
  1899. def _define_include(self, obj):
  1900. includeclause = obj.dialect_options["postgresql"]["include"]
  1901. if not includeclause:
  1902. return ""
  1903. inclusions = [
  1904. obj.table.c[col] if isinstance(col, str) else col
  1905. for col in includeclause
  1906. ]
  1907. return " INCLUDE (%s)" % ", ".join(
  1908. [self.preparer.quote(c.name) for c in inclusions]
  1909. )
  1910. def visit_check_constraint(self, constraint, **kw):
  1911. if constraint._type_bound:
  1912. typ = list(constraint.columns)[0].type
  1913. if (
  1914. isinstance(typ, sqltypes.ARRAY)
  1915. and isinstance(typ.item_type, sqltypes.Enum)
  1916. and not typ.item_type.native_enum
  1917. ):
  1918. raise exc.CompileError(
  1919. "PostgreSQL dialect cannot produce the CHECK constraint "
  1920. "for ARRAY of non-native ENUM; please specify "
  1921. "create_constraint=False on this Enum datatype."
  1922. )
  1923. text = super().visit_check_constraint(constraint)
  1924. text += self._define_constraint_validity(constraint)
  1925. return text
  1926. def visit_foreign_key_constraint(self, constraint, **kw):
  1927. text = super().visit_foreign_key_constraint(constraint)
  1928. text += self._define_constraint_validity(constraint)
  1929. return text
  1930. def visit_primary_key_constraint(self, constraint, **kw):
  1931. text = self.define_constraint_preamble(constraint, **kw)
  1932. text += self.define_primary_key_body(constraint, **kw)
  1933. text += self._define_include(constraint)
  1934. text += self.define_constraint_deferrability(constraint)
  1935. return text
  1936. def visit_unique_constraint(self, constraint, **kw):
  1937. if len(constraint) == 0:
  1938. return ""
  1939. text = self.define_constraint_preamble(constraint, **kw)
  1940. text += self.define_unique_body(constraint, **kw)
  1941. text += self._define_include(constraint)
  1942. text += self.define_constraint_deferrability(constraint)
  1943. return text
  1944. @util.memoized_property
  1945. def _fk_ondelete_pattern(self):
  1946. return re.compile(
  1947. r"^(?:RESTRICT|CASCADE|SET (?:NULL|DEFAULT)(?:\s*\(.+\))?"
  1948. r"|NO ACTION)$",
  1949. re.I,
  1950. )
  1951. def define_constraint_ondelete_cascade(self, constraint):
  1952. return " ON DELETE %s" % self.preparer.validate_sql_phrase(
  1953. constraint.ondelete, self._fk_ondelete_pattern
  1954. )
  1955. def visit_create_enum_type(self, create, **kw):
  1956. type_ = create.element
  1957. return "CREATE TYPE %s AS ENUM (%s)" % (
  1958. self.preparer.format_type(type_),
  1959. ", ".join(
  1960. self.sql_compiler.process(sql.literal(e), literal_binds=True)
  1961. for e in type_.enums
  1962. ),
  1963. )
  1964. def visit_drop_enum_type(self, drop, **kw):
  1965. type_ = drop.element
  1966. return "DROP TYPE %s" % (self.preparer.format_type(type_))
  1967. def visit_create_domain_type(self, create, **kw):
  1968. domain: DOMAIN = create.element
  1969. options = []
  1970. if domain.collation is not None:
  1971. options.append(f"COLLATE {self.preparer.quote(domain.collation)}")
  1972. if domain.default is not None:
  1973. default = self.render_default_string(domain.default)
  1974. options.append(f"DEFAULT {default}")
  1975. if domain.constraint_name is not None:
  1976. name = self.preparer.truncate_and_render_constraint_name(
  1977. domain.constraint_name
  1978. )
  1979. options.append(f"CONSTRAINT {name}")
  1980. if domain.not_null:
  1981. options.append("NOT NULL")
  1982. if domain.check is not None:
  1983. check = self.sql_compiler.process(
  1984. domain.check, include_table=False, literal_binds=True
  1985. )
  1986. options.append(f"CHECK ({check})")
  1987. return (
  1988. f"CREATE DOMAIN {self.preparer.format_type(domain)} AS "
  1989. f"{self.type_compiler.process(domain.data_type)} "
  1990. f"{' '.join(options)}"
  1991. )
  1992. def visit_drop_domain_type(self, drop, **kw):
  1993. domain = drop.element
  1994. return f"DROP DOMAIN {self.preparer.format_type(domain)}"
  1995. def visit_create_index(self, create, **kw):
  1996. preparer = self.preparer
  1997. index = create.element
  1998. self._verify_index_table(index)
  1999. text = "CREATE "
  2000. if index.unique:
  2001. text += "UNIQUE "
  2002. text += "INDEX "
  2003. if self.dialect._supports_create_index_concurrently:
  2004. concurrently = index.dialect_options["postgresql"]["concurrently"]
  2005. if concurrently:
  2006. text += "CONCURRENTLY "
  2007. if create.if_not_exists:
  2008. text += "IF NOT EXISTS "
  2009. text += "%s ON %s " % (
  2010. self._prepared_index_name(index, include_schema=False),
  2011. preparer.format_table(index.table),
  2012. )
  2013. using = index.dialect_options["postgresql"]["using"]
  2014. if using:
  2015. text += (
  2016. "USING %s "
  2017. % self.preparer.validate_sql_phrase(using, IDX_USING).lower()
  2018. )
  2019. ops = index.dialect_options["postgresql"]["ops"]
  2020. text += "(%s)" % (
  2021. ", ".join(
  2022. [
  2023. self.sql_compiler.process(
  2024. (
  2025. expr.self_group()
  2026. if not isinstance(expr, expression.ColumnClause)
  2027. else expr
  2028. ),
  2029. include_table=False,
  2030. literal_binds=True,
  2031. )
  2032. + (
  2033. (" " + ops[expr.key])
  2034. if hasattr(expr, "key") and expr.key in ops
  2035. else ""
  2036. )
  2037. for expr in index.expressions
  2038. ]
  2039. )
  2040. )
  2041. text += self._define_include(index)
  2042. nulls_not_distinct = index.dialect_options["postgresql"][
  2043. "nulls_not_distinct"
  2044. ]
  2045. if nulls_not_distinct is True:
  2046. text += " NULLS NOT DISTINCT"
  2047. elif nulls_not_distinct is False:
  2048. text += " NULLS DISTINCT"
  2049. withclause = index.dialect_options["postgresql"]["with"]
  2050. if withclause:
  2051. text += " WITH (%s)" % (
  2052. ", ".join(
  2053. [
  2054. "%s = %s" % storage_parameter
  2055. for storage_parameter in withclause.items()
  2056. ]
  2057. )
  2058. )
  2059. tablespace_name = index.dialect_options["postgresql"]["tablespace"]
  2060. if tablespace_name:
  2061. text += " TABLESPACE %s" % preparer.quote(tablespace_name)
  2062. whereclause = index.dialect_options["postgresql"]["where"]
  2063. if whereclause is not None:
  2064. whereclause = coercions.expect(
  2065. roles.DDLExpressionRole, whereclause
  2066. )
  2067. where_compiled = self.sql_compiler.process(
  2068. whereclause, include_table=False, literal_binds=True
  2069. )
  2070. text += " WHERE " + where_compiled
  2071. return text
  2072. def define_unique_constraint_distinct(self, constraint, **kw):
  2073. nulls_not_distinct = constraint.dialect_options["postgresql"][
  2074. "nulls_not_distinct"
  2075. ]
  2076. if nulls_not_distinct is True:
  2077. nulls_not_distinct_param = "NULLS NOT DISTINCT "
  2078. elif nulls_not_distinct is False:
  2079. nulls_not_distinct_param = "NULLS DISTINCT "
  2080. else:
  2081. nulls_not_distinct_param = ""
  2082. return nulls_not_distinct_param
  2083. def visit_drop_index(self, drop, **kw):
  2084. index = drop.element
  2085. text = "\nDROP INDEX "
  2086. if self.dialect._supports_drop_index_concurrently:
  2087. concurrently = index.dialect_options["postgresql"]["concurrently"]
  2088. if concurrently:
  2089. text += "CONCURRENTLY "
  2090. if drop.if_exists:
  2091. text += "IF EXISTS "
  2092. text += self._prepared_index_name(index, include_schema=True)
  2093. return text
  2094. def visit_exclude_constraint(self, constraint, **kw):
  2095. text = ""
  2096. if constraint.name is not None:
  2097. text += "CONSTRAINT %s " % self.preparer.format_constraint(
  2098. constraint
  2099. )
  2100. elements = []
  2101. kw["include_table"] = False
  2102. kw["literal_binds"] = True
  2103. for expr, name, op in constraint._render_exprs:
  2104. exclude_element = self.sql_compiler.process(expr, **kw) + (
  2105. (" " + constraint.ops[expr.key])
  2106. if hasattr(expr, "key") and expr.key in constraint.ops
  2107. else ""
  2108. )
  2109. elements.append("%s WITH %s" % (exclude_element, op))
  2110. text += "EXCLUDE USING %s (%s)" % (
  2111. self.preparer.validate_sql_phrase(
  2112. constraint.using, IDX_USING
  2113. ).lower(),
  2114. ", ".join(elements),
  2115. )
  2116. if constraint.where is not None:
  2117. text += " WHERE (%s)" % self.sql_compiler.process(
  2118. constraint.where, literal_binds=True
  2119. )
  2120. text += self.define_constraint_deferrability(constraint)
  2121. return text
  2122. def post_create_table(self, table):
  2123. table_opts = []
  2124. pg_opts = table.dialect_options["postgresql"]
  2125. inherits = pg_opts.get("inherits")
  2126. if inherits is not None:
  2127. if not isinstance(inherits, (list, tuple)):
  2128. inherits = (inherits,)
  2129. table_opts.append(
  2130. "\n INHERITS ( "
  2131. + ", ".join(self.preparer.quote(name) for name in inherits)
  2132. + " )"
  2133. )
  2134. if pg_opts["partition_by"]:
  2135. table_opts.append("\n PARTITION BY %s" % pg_opts["partition_by"])
  2136. if pg_opts["using"]:
  2137. table_opts.append("\n USING %s" % pg_opts["using"])
  2138. if pg_opts["with_oids"] is True:
  2139. table_opts.append("\n WITH OIDS")
  2140. elif pg_opts["with_oids"] is False:
  2141. table_opts.append("\n WITHOUT OIDS")
  2142. if pg_opts["on_commit"]:
  2143. on_commit_options = pg_opts["on_commit"].replace("_", " ").upper()
  2144. table_opts.append("\n ON COMMIT %s" % on_commit_options)
  2145. if pg_opts["tablespace"]:
  2146. tablespace_name = pg_opts["tablespace"]
  2147. table_opts.append(
  2148. "\n TABLESPACE %s" % self.preparer.quote(tablespace_name)
  2149. )
  2150. return "".join(table_opts)
  2151. def visit_computed_column(self, generated, **kw):
  2152. if generated.persisted is False:
  2153. raise exc.CompileError(
  2154. "PostrgreSQL computed columns do not support 'virtual' "
  2155. "persistence; set the 'persisted' flag to None or True for "
  2156. "PostgreSQL support."
  2157. )
  2158. return "GENERATED ALWAYS AS (%s) STORED" % self.sql_compiler.process(
  2159. generated.sqltext, include_table=False, literal_binds=True
  2160. )
  2161. def visit_create_sequence(self, create, **kw):
  2162. prefix = None
  2163. if create.element.data_type is not None:
  2164. prefix = " AS %s" % self.type_compiler.process(
  2165. create.element.data_type
  2166. )
  2167. return super().visit_create_sequence(create, prefix=prefix, **kw)
  2168. def _can_comment_on_constraint(self, ddl_instance):
  2169. constraint = ddl_instance.element
  2170. if constraint.name is None:
  2171. raise exc.CompileError(
  2172. f"Can't emit COMMENT ON for constraint {constraint!r}: "
  2173. "it has no name"
  2174. )
  2175. if constraint.table is None:
  2176. raise exc.CompileError(
  2177. f"Can't emit COMMENT ON for constraint {constraint!r}: "
  2178. "it has no associated table"
  2179. )
  2180. def visit_set_constraint_comment(self, create, **kw):
  2181. self._can_comment_on_constraint(create)
  2182. return "COMMENT ON CONSTRAINT %s ON %s IS %s" % (
  2183. self.preparer.format_constraint(create.element),
  2184. self.preparer.format_table(create.element.table),
  2185. self.sql_compiler.render_literal_value(
  2186. create.element.comment, sqltypes.String()
  2187. ),
  2188. )
  2189. def visit_drop_constraint_comment(self, drop, **kw):
  2190. self._can_comment_on_constraint(drop)
  2191. return "COMMENT ON CONSTRAINT %s ON %s IS NULL" % (
  2192. self.preparer.format_constraint(drop.element),
  2193. self.preparer.format_table(drop.element.table),
  2194. )
  2195. class PGTypeCompiler(compiler.GenericTypeCompiler):
  2196. def visit_TSVECTOR(self, type_, **kw):
  2197. return "TSVECTOR"
  2198. def visit_TSQUERY(self, type_, **kw):
  2199. return "TSQUERY"
  2200. def visit_INET(self, type_, **kw):
  2201. return "INET"
  2202. def visit_CIDR(self, type_, **kw):
  2203. return "CIDR"
  2204. def visit_CITEXT(self, type_, **kw):
  2205. return "CITEXT"
  2206. def visit_MACADDR(self, type_, **kw):
  2207. return "MACADDR"
  2208. def visit_MACADDR8(self, type_, **kw):
  2209. return "MACADDR8"
  2210. def visit_MONEY(self, type_, **kw):
  2211. return "MONEY"
  2212. def visit_OID(self, type_, **kw):
  2213. return "OID"
  2214. def visit_REGCONFIG(self, type_, **kw):
  2215. return "REGCONFIG"
  2216. def visit_REGCLASS(self, type_, **kw):
  2217. return "REGCLASS"
  2218. def visit_FLOAT(self, type_, **kw):
  2219. if not type_.precision:
  2220. return "FLOAT"
  2221. else:
  2222. return "FLOAT(%(precision)s)" % {"precision": type_.precision}
  2223. def visit_double(self, type_, **kw):
  2224. return self.visit_DOUBLE_PRECISION(type, **kw)
  2225. def visit_BIGINT(self, type_, **kw):
  2226. return "BIGINT"
  2227. def visit_HSTORE(self, type_, **kw):
  2228. return "HSTORE"
  2229. def visit_JSON(self, type_, **kw):
  2230. return "JSON"
  2231. def visit_JSONB(self, type_, **kw):
  2232. return "JSONB"
  2233. def visit_INT4MULTIRANGE(self, type_, **kw):
  2234. return "INT4MULTIRANGE"
  2235. def visit_INT8MULTIRANGE(self, type_, **kw):
  2236. return "INT8MULTIRANGE"
  2237. def visit_NUMMULTIRANGE(self, type_, **kw):
  2238. return "NUMMULTIRANGE"
  2239. def visit_DATEMULTIRANGE(self, type_, **kw):
  2240. return "DATEMULTIRANGE"
  2241. def visit_TSMULTIRANGE(self, type_, **kw):
  2242. return "TSMULTIRANGE"
  2243. def visit_TSTZMULTIRANGE(self, type_, **kw):
  2244. return "TSTZMULTIRANGE"
  2245. def visit_INT4RANGE(self, type_, **kw):
  2246. return "INT4RANGE"
  2247. def visit_INT8RANGE(self, type_, **kw):
  2248. return "INT8RANGE"
  2249. def visit_NUMRANGE(self, type_, **kw):
  2250. return "NUMRANGE"
  2251. def visit_DATERANGE(self, type_, **kw):
  2252. return "DATERANGE"
  2253. def visit_TSRANGE(self, type_, **kw):
  2254. return "TSRANGE"
  2255. def visit_TSTZRANGE(self, type_, **kw):
  2256. return "TSTZRANGE"
  2257. def visit_json_int_index(self, type_, **kw):
  2258. return "INT"
  2259. def visit_json_str_index(self, type_, **kw):
  2260. return "TEXT"
  2261. def visit_datetime(self, type_, **kw):
  2262. return self.visit_TIMESTAMP(type_, **kw)
  2263. def visit_enum(self, type_, **kw):
  2264. if not type_.native_enum or not self.dialect.supports_native_enum:
  2265. return super().visit_enum(type_, **kw)
  2266. else:
  2267. return self.visit_ENUM(type_, **kw)
  2268. def visit_ENUM(self, type_, identifier_preparer=None, **kw):
  2269. if identifier_preparer is None:
  2270. identifier_preparer = self.dialect.identifier_preparer
  2271. return identifier_preparer.format_type(type_)
  2272. def visit_DOMAIN(self, type_, identifier_preparer=None, **kw):
  2273. if identifier_preparer is None:
  2274. identifier_preparer = self.dialect.identifier_preparer
  2275. return identifier_preparer.format_type(type_)
  2276. def visit_TIMESTAMP(self, type_, **kw):
  2277. return "TIMESTAMP%s %s" % (
  2278. (
  2279. "(%d)" % type_.precision
  2280. if getattr(type_, "precision", None) is not None
  2281. else ""
  2282. ),
  2283. (type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE",
  2284. )
  2285. def visit_TIME(self, type_, **kw):
  2286. return "TIME%s %s" % (
  2287. (
  2288. "(%d)" % type_.precision
  2289. if getattr(type_, "precision", None) is not None
  2290. else ""
  2291. ),
  2292. (type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE",
  2293. )
  2294. def visit_INTERVAL(self, type_, **kw):
  2295. text = "INTERVAL"
  2296. if type_.fields is not None:
  2297. text += " " + type_.fields
  2298. if type_.precision is not None:
  2299. text += " (%d)" % type_.precision
  2300. return text
  2301. def visit_BIT(self, type_, **kw):
  2302. if type_.varying:
  2303. compiled = "BIT VARYING"
  2304. if type_.length is not None:
  2305. compiled += "(%d)" % type_.length
  2306. else:
  2307. compiled = "BIT(%d)" % type_.length
  2308. return compiled
  2309. def visit_uuid(self, type_, **kw):
  2310. if type_.native_uuid:
  2311. return self.visit_UUID(type_, **kw)
  2312. else:
  2313. return super().visit_uuid(type_, **kw)
  2314. def visit_UUID(self, type_, **kw):
  2315. return "UUID"
  2316. def visit_large_binary(self, type_, **kw):
  2317. return self.visit_BYTEA(type_, **kw)
  2318. def visit_BYTEA(self, type_, **kw):
  2319. return "BYTEA"
  2320. def visit_ARRAY(self, type_, **kw):
  2321. inner = self.process(type_.item_type, **kw)
  2322. return re.sub(
  2323. r"((?: COLLATE.*)?)$",
  2324. (
  2325. r"%s\1"
  2326. % (
  2327. "[]"
  2328. * (type_.dimensions if type_.dimensions is not None else 1)
  2329. )
  2330. ),
  2331. inner,
  2332. count=1,
  2333. )
  2334. def visit_json_path(self, type_, **kw):
  2335. return self.visit_JSONPATH(type_, **kw)
  2336. def visit_JSONPATH(self, type_, **kw):
  2337. return "JSONPATH"
  2338. class PGIdentifierPreparer(compiler.IdentifierPreparer):
  2339. reserved_words = RESERVED_WORDS
  2340. def _unquote_identifier(self, value):
  2341. if value[0] == self.initial_quote:
  2342. value = value[1:-1].replace(
  2343. self.escape_to_quote, self.escape_quote
  2344. )
  2345. return value
  2346. def format_type(self, type_, use_schema=True):
  2347. if not type_.name:
  2348. raise exc.CompileError(
  2349. f"PostgreSQL {type_.__class__.__name__} type requires a name."
  2350. )
  2351. name = self.quote(type_.name)
  2352. effective_schema = self.schema_for_object(type_)
  2353. if (
  2354. not self.omit_schema
  2355. and use_schema
  2356. and effective_schema is not None
  2357. ):
  2358. name = f"{self.quote_schema(effective_schema)}.{name}"
  2359. return name
  2360. class ReflectedNamedType(TypedDict):
  2361. """Represents a reflected named type."""
  2362. name: str
  2363. """Name of the type."""
  2364. schema: str
  2365. """The schema of the type."""
  2366. visible: bool
  2367. """Indicates if this type is in the current search path."""
  2368. class ReflectedDomainConstraint(TypedDict):
  2369. """Represents a reflect check constraint of a domain."""
  2370. name: str
  2371. """Name of the constraint."""
  2372. check: str
  2373. """The check constraint text."""
  2374. class ReflectedDomain(ReflectedNamedType):
  2375. """Represents a reflected enum."""
  2376. type: str
  2377. """The string name of the underlying data type of the domain."""
  2378. nullable: bool
  2379. """Indicates if the domain allows null or not."""
  2380. default: Optional[str]
  2381. """The string representation of the default value of this domain
  2382. or ``None`` if none present.
  2383. """
  2384. constraints: List[ReflectedDomainConstraint]
  2385. """The constraints defined in the domain, if any.
  2386. The constraint are in order of evaluation by postgresql.
  2387. """
  2388. collation: Optional[str]
  2389. """The collation for the domain."""
  2390. class ReflectedEnum(ReflectedNamedType):
  2391. """Represents a reflected enum."""
  2392. labels: List[str]
  2393. """The labels that compose the enum."""
  2394. class PGInspector(reflection.Inspector):
  2395. dialect: PGDialect
  2396. def get_table_oid(
  2397. self, table_name: str, schema: Optional[str] = None
  2398. ) -> int:
  2399. """Return the OID for the given table name.
  2400. :param table_name: string name of the table. For special quoting,
  2401. use :class:`.quoted_name`.
  2402. :param schema: string schema name; if omitted, uses the default schema
  2403. of the database connection. For special quoting,
  2404. use :class:`.quoted_name`.
  2405. """
  2406. with self._operation_context() as conn:
  2407. return self.dialect.get_table_oid(
  2408. conn, table_name, schema, info_cache=self.info_cache
  2409. )
  2410. def get_domains(
  2411. self, schema: Optional[str] = None
  2412. ) -> List[ReflectedDomain]:
  2413. """Return a list of DOMAIN objects.
  2414. Each member is a dictionary containing these fields:
  2415. * name - name of the domain
  2416. * schema - the schema name for the domain.
  2417. * visible - boolean, whether or not this domain is visible
  2418. in the default search path.
  2419. * type - the type defined by this domain.
  2420. * nullable - Indicates if this domain can be ``NULL``.
  2421. * default - The default value of the domain or ``None`` if the
  2422. domain has no default.
  2423. * constraints - A list of dict with the constraint defined by this
  2424. domain. Each element contains two keys: ``name`` of the
  2425. constraint and ``check`` with the constraint text.
  2426. :param schema: schema name. If None, the default schema
  2427. (typically 'public') is used. May also be set to ``'*'`` to
  2428. indicate load domains for all schemas.
  2429. .. versionadded:: 2.0
  2430. """
  2431. with self._operation_context() as conn:
  2432. return self.dialect._load_domains(
  2433. conn, schema, info_cache=self.info_cache
  2434. )
  2435. def get_enums(self, schema: Optional[str] = None) -> List[ReflectedEnum]:
  2436. """Return a list of ENUM objects.
  2437. Each member is a dictionary containing these fields:
  2438. * name - name of the enum
  2439. * schema - the schema name for the enum.
  2440. * visible - boolean, whether or not this enum is visible
  2441. in the default search path.
  2442. * labels - a list of string labels that apply to the enum.
  2443. :param schema: schema name. If None, the default schema
  2444. (typically 'public') is used. May also be set to ``'*'`` to
  2445. indicate load enums for all schemas.
  2446. """
  2447. with self._operation_context() as conn:
  2448. return self.dialect._load_enums(
  2449. conn, schema, info_cache=self.info_cache
  2450. )
  2451. def get_foreign_table_names(
  2452. self, schema: Optional[str] = None
  2453. ) -> List[str]:
  2454. """Return a list of FOREIGN TABLE names.
  2455. Behavior is similar to that of
  2456. :meth:`_reflection.Inspector.get_table_names`,
  2457. except that the list is limited to those tables that report a
  2458. ``relkind`` value of ``f``.
  2459. """
  2460. with self._operation_context() as conn:
  2461. return self.dialect._get_foreign_table_names(
  2462. conn, schema, info_cache=self.info_cache
  2463. )
  2464. def has_type(
  2465. self, type_name: str, schema: Optional[str] = None, **kw: Any
  2466. ) -> bool:
  2467. """Return if the database has the specified type in the provided
  2468. schema.
  2469. :param type_name: the type to check.
  2470. :param schema: schema name. If None, the default schema
  2471. (typically 'public') is used. May also be set to ``'*'`` to
  2472. check in all schemas.
  2473. .. versionadded:: 2.0
  2474. """
  2475. with self._operation_context() as conn:
  2476. return self.dialect.has_type(
  2477. conn, type_name, schema, info_cache=self.info_cache
  2478. )
  2479. class PGExecutionContext(default.DefaultExecutionContext):
  2480. def fire_sequence(self, seq, type_):
  2481. return self._execute_scalar(
  2482. (
  2483. "select nextval('%s')"
  2484. % self.identifier_preparer.format_sequence(seq)
  2485. ),
  2486. type_,
  2487. )
  2488. def get_insert_default(self, column):
  2489. if column.primary_key and column is column.table._autoincrement_column:
  2490. if column.server_default and column.server_default.has_argument:
  2491. # pre-execute passive defaults on primary key columns
  2492. return self._execute_scalar(
  2493. "select %s" % column.server_default.arg, column.type
  2494. )
  2495. elif column.default is None or (
  2496. column.default.is_sequence and column.default.optional
  2497. ):
  2498. # execute the sequence associated with a SERIAL primary
  2499. # key column. for non-primary-key SERIAL, the ID just
  2500. # generates server side.
  2501. try:
  2502. seq_name = column._postgresql_seq_name
  2503. except AttributeError:
  2504. tab = column.table.name
  2505. col = column.name
  2506. tab = tab[0 : 29 + max(0, (29 - len(col)))]
  2507. col = col[0 : 29 + max(0, (29 - len(tab)))]
  2508. name = "%s_%s_seq" % (tab, col)
  2509. column._postgresql_seq_name = seq_name = name
  2510. if column.table is not None:
  2511. effective_schema = self.connection.schema_for_object(
  2512. column.table
  2513. )
  2514. else:
  2515. effective_schema = None
  2516. if effective_schema is not None:
  2517. exc = 'select nextval(\'"%s"."%s"\')' % (
  2518. effective_schema,
  2519. seq_name,
  2520. )
  2521. else:
  2522. exc = "select nextval('\"%s\"')" % (seq_name,)
  2523. return self._execute_scalar(exc, column.type)
  2524. return super().get_insert_default(column)
  2525. class PGReadOnlyConnectionCharacteristic(
  2526. characteristics.ConnectionCharacteristic
  2527. ):
  2528. transactional = True
  2529. def reset_characteristic(self, dialect, dbapi_conn):
  2530. dialect.set_readonly(dbapi_conn, False)
  2531. def set_characteristic(self, dialect, dbapi_conn, value):
  2532. dialect.set_readonly(dbapi_conn, value)
  2533. def get_characteristic(self, dialect, dbapi_conn):
  2534. return dialect.get_readonly(dbapi_conn)
  2535. class PGDeferrableConnectionCharacteristic(
  2536. characteristics.ConnectionCharacteristic
  2537. ):
  2538. transactional = True
  2539. def reset_characteristic(self, dialect, dbapi_conn):
  2540. dialect.set_deferrable(dbapi_conn, False)
  2541. def set_characteristic(self, dialect, dbapi_conn, value):
  2542. dialect.set_deferrable(dbapi_conn, value)
  2543. def get_characteristic(self, dialect, dbapi_conn):
  2544. return dialect.get_deferrable(dbapi_conn)
  2545. class PGDialect(default.DefaultDialect):
  2546. name = "postgresql"
  2547. supports_statement_cache = True
  2548. supports_alter = True
  2549. max_identifier_length = 63
  2550. supports_sane_rowcount = True
  2551. bind_typing = interfaces.BindTyping.RENDER_CASTS
  2552. supports_native_enum = True
  2553. supports_native_boolean = True
  2554. supports_native_uuid = True
  2555. supports_smallserial = True
  2556. supports_sequences = True
  2557. sequences_optional = True
  2558. preexecute_autoincrement_sequences = True
  2559. postfetch_lastrowid = False
  2560. use_insertmanyvalues = True
  2561. returns_native_bytes = True
  2562. insertmanyvalues_implicit_sentinel = (
  2563. InsertmanyvaluesSentinelOpts.ANY_AUTOINCREMENT
  2564. | InsertmanyvaluesSentinelOpts.USE_INSERT_FROM_SELECT
  2565. | InsertmanyvaluesSentinelOpts.RENDER_SELECT_COL_CASTS
  2566. )
  2567. supports_comments = True
  2568. supports_constraint_comments = True
  2569. supports_default_values = True
  2570. supports_default_metavalue = True
  2571. supports_empty_insert = False
  2572. supports_multivalues_insert = True
  2573. supports_identity_columns = True
  2574. default_paramstyle = "pyformat"
  2575. ischema_names = ischema_names
  2576. colspecs = colspecs
  2577. statement_compiler = PGCompiler
  2578. ddl_compiler = PGDDLCompiler
  2579. type_compiler_cls = PGTypeCompiler
  2580. preparer = PGIdentifierPreparer
  2581. execution_ctx_cls = PGExecutionContext
  2582. inspector = PGInspector
  2583. update_returning = True
  2584. delete_returning = True
  2585. insert_returning = True
  2586. update_returning_multifrom = True
  2587. delete_returning_multifrom = True
  2588. connection_characteristics = (
  2589. default.DefaultDialect.connection_characteristics
  2590. )
  2591. connection_characteristics = connection_characteristics.union(
  2592. {
  2593. "postgresql_readonly": PGReadOnlyConnectionCharacteristic(),
  2594. "postgresql_deferrable": PGDeferrableConnectionCharacteristic(),
  2595. }
  2596. )
  2597. construct_arguments = [
  2598. (
  2599. schema.Index,
  2600. {
  2601. "using": False,
  2602. "include": None,
  2603. "where": None,
  2604. "ops": {},
  2605. "concurrently": False,
  2606. "with": {},
  2607. "tablespace": None,
  2608. "nulls_not_distinct": None,
  2609. },
  2610. ),
  2611. (
  2612. schema.Table,
  2613. {
  2614. "ignore_search_path": False,
  2615. "tablespace": None,
  2616. "partition_by": None,
  2617. "with_oids": None,
  2618. "on_commit": None,
  2619. "inherits": None,
  2620. "using": None,
  2621. },
  2622. ),
  2623. (
  2624. schema.CheckConstraint,
  2625. {
  2626. "not_valid": False,
  2627. },
  2628. ),
  2629. (
  2630. schema.ForeignKeyConstraint,
  2631. {
  2632. "not_valid": False,
  2633. },
  2634. ),
  2635. (
  2636. schema.PrimaryKeyConstraint,
  2637. {"include": None},
  2638. ),
  2639. (
  2640. schema.UniqueConstraint,
  2641. {
  2642. "include": None,
  2643. "nulls_not_distinct": None,
  2644. },
  2645. ),
  2646. ]
  2647. reflection_options = ("postgresql_ignore_search_path",)
  2648. _backslash_escapes = True
  2649. _supports_create_index_concurrently = True
  2650. _supports_drop_index_concurrently = True
  2651. _supports_jsonb_subscripting = True
  2652. def __init__(
  2653. self,
  2654. native_inet_types=None,
  2655. json_serializer=None,
  2656. json_deserializer=None,
  2657. **kwargs,
  2658. ):
  2659. default.DefaultDialect.__init__(self, **kwargs)
  2660. self._native_inet_types = native_inet_types
  2661. self._json_deserializer = json_deserializer
  2662. self._json_serializer = json_serializer
  2663. def initialize(self, connection):
  2664. super().initialize(connection)
  2665. # https://www.postgresql.org/docs/9.3/static/release-9-2.html#AEN116689
  2666. self.supports_smallserial = self.server_version_info >= (9, 2)
  2667. self._set_backslash_escapes(connection)
  2668. self._supports_drop_index_concurrently = self.server_version_info >= (
  2669. 9,
  2670. 2,
  2671. )
  2672. self.supports_identity_columns = self.server_version_info >= (10,)
  2673. self._supports_jsonb_subscripting = self.server_version_info >= (14,)
  2674. def get_isolation_level_values(self, dbapi_conn):
  2675. # note the generic dialect doesn't have AUTOCOMMIT, however
  2676. # all postgresql dialects should include AUTOCOMMIT.
  2677. return (
  2678. "SERIALIZABLE",
  2679. "READ UNCOMMITTED",
  2680. "READ COMMITTED",
  2681. "REPEATABLE READ",
  2682. )
  2683. def set_isolation_level(self, dbapi_connection, level):
  2684. cursor = dbapi_connection.cursor()
  2685. cursor.execute(
  2686. "SET SESSION CHARACTERISTICS AS TRANSACTION "
  2687. f"ISOLATION LEVEL {level}"
  2688. )
  2689. cursor.execute("COMMIT")
  2690. cursor.close()
  2691. def get_isolation_level(self, dbapi_connection):
  2692. cursor = dbapi_connection.cursor()
  2693. cursor.execute("show transaction isolation level")
  2694. val = cursor.fetchone()[0]
  2695. cursor.close()
  2696. return val.upper()
  2697. def set_readonly(self, connection, value):
  2698. raise NotImplementedError()
  2699. def get_readonly(self, connection):
  2700. raise NotImplementedError()
  2701. def set_deferrable(self, connection, value):
  2702. raise NotImplementedError()
  2703. def get_deferrable(self, connection):
  2704. raise NotImplementedError()
  2705. def _split_multihost_from_url(self, url: URL) -> Union[
  2706. Tuple[None, None],
  2707. Tuple[Tuple[Optional[str], ...], Tuple[Optional[int], ...]],
  2708. ]:
  2709. hosts: Optional[Tuple[Optional[str], ...]] = None
  2710. ports_str: Union[str, Tuple[Optional[str], ...], None] = None
  2711. integrated_multihost = False
  2712. if "host" in url.query:
  2713. if isinstance(url.query["host"], (list, tuple)):
  2714. integrated_multihost = True
  2715. hosts, ports_str = zip(
  2716. *[
  2717. token.split(":") if ":" in token else (token, None)
  2718. for token in url.query["host"]
  2719. ]
  2720. )
  2721. elif isinstance(url.query["host"], str):
  2722. hosts = tuple(url.query["host"].split(","))
  2723. if (
  2724. "port" not in url.query
  2725. and len(hosts) == 1
  2726. and ":" in hosts[0]
  2727. ):
  2728. # internet host is alphanumeric plus dots or hyphens.
  2729. # this is essentially rfc1123, which refers to rfc952.
  2730. # https://stackoverflow.com/questions/3523028/
  2731. # valid-characters-of-a-hostname
  2732. host_port_match = re.match(
  2733. r"^([a-zA-Z0-9\-\.]*)(?:\:(\d*))?$", hosts[0]
  2734. )
  2735. if host_port_match:
  2736. integrated_multihost = True
  2737. h, p = host_port_match.group(1, 2)
  2738. if TYPE_CHECKING:
  2739. assert isinstance(h, str)
  2740. assert isinstance(p, str)
  2741. hosts = (h,)
  2742. ports_str = cast(
  2743. "Tuple[Optional[str], ...]", (p,) if p else (None,)
  2744. )
  2745. if "port" in url.query:
  2746. if integrated_multihost:
  2747. raise exc.ArgumentError(
  2748. "Can't mix 'multihost' formats together; use "
  2749. '"host=h1,h2,h3&port=p1,p2,p3" or '
  2750. '"host=h1:p1&host=h2:p2&host=h3:p3" separately'
  2751. )
  2752. if isinstance(url.query["port"], (list, tuple)):
  2753. ports_str = url.query["port"]
  2754. elif isinstance(url.query["port"], str):
  2755. ports_str = tuple(url.query["port"].split(","))
  2756. ports: Optional[Tuple[Optional[int], ...]] = None
  2757. if ports_str:
  2758. try:
  2759. ports = tuple(int(x) if x else None for x in ports_str)
  2760. except ValueError:
  2761. raise exc.ArgumentError(
  2762. f"Received non-integer port arguments: {ports_str}"
  2763. ) from None
  2764. if ports and (
  2765. (not hosts and len(ports) > 1)
  2766. or (
  2767. hosts
  2768. and ports
  2769. and len(hosts) != len(ports)
  2770. and (len(hosts) > 1 or len(ports) > 1)
  2771. )
  2772. ):
  2773. raise exc.ArgumentError("number of hosts and ports don't match")
  2774. if hosts is not None:
  2775. if ports is None:
  2776. ports = tuple(None for _ in hosts)
  2777. return hosts, ports # type: ignore
  2778. def do_begin_twophase(self, connection, xid):
  2779. self.do_begin(connection.connection)
  2780. def do_prepare_twophase(self, connection, xid):
  2781. connection.exec_driver_sql("PREPARE TRANSACTION '%s'" % xid)
  2782. def do_rollback_twophase(
  2783. self, connection, xid, is_prepared=True, recover=False
  2784. ):
  2785. if is_prepared:
  2786. if recover:
  2787. # FIXME: ugly hack to get out of transaction
  2788. # context when committing recoverable transactions
  2789. # Must find out a way how to make the dbapi not
  2790. # open a transaction.
  2791. connection.exec_driver_sql("ROLLBACK")
  2792. connection.exec_driver_sql("ROLLBACK PREPARED '%s'" % xid)
  2793. connection.exec_driver_sql("BEGIN")
  2794. self.do_rollback(connection.connection)
  2795. else:
  2796. self.do_rollback(connection.connection)
  2797. def do_commit_twophase(
  2798. self, connection, xid, is_prepared=True, recover=False
  2799. ):
  2800. if is_prepared:
  2801. if recover:
  2802. connection.exec_driver_sql("ROLLBACK")
  2803. connection.exec_driver_sql("COMMIT PREPARED '%s'" % xid)
  2804. connection.exec_driver_sql("BEGIN")
  2805. self.do_rollback(connection.connection)
  2806. else:
  2807. self.do_commit(connection.connection)
  2808. def do_recover_twophase(self, connection):
  2809. return connection.scalars(
  2810. sql.text("SELECT gid FROM pg_prepared_xacts")
  2811. ).all()
  2812. def _get_default_schema_name(self, connection):
  2813. return connection.exec_driver_sql("select current_schema()").scalar()
  2814. @reflection.cache
  2815. def has_schema(self, connection, schema, **kw):
  2816. query = select(pg_catalog.pg_namespace.c.nspname).where(
  2817. pg_catalog.pg_namespace.c.nspname == schema
  2818. )
  2819. return bool(connection.scalar(query))
  2820. def _pg_class_filter_scope_schema(
  2821. self, query, schema, scope, pg_class_table=None
  2822. ):
  2823. if pg_class_table is None:
  2824. pg_class_table = pg_catalog.pg_class
  2825. query = query.join(
  2826. pg_catalog.pg_namespace,
  2827. pg_catalog.pg_namespace.c.oid == pg_class_table.c.relnamespace,
  2828. )
  2829. if scope is ObjectScope.DEFAULT:
  2830. query = query.where(pg_class_table.c.relpersistence != "t")
  2831. elif scope is ObjectScope.TEMPORARY:
  2832. query = query.where(pg_class_table.c.relpersistence == "t")
  2833. if schema is None:
  2834. query = query.where(
  2835. pg_catalog.pg_table_is_visible(pg_class_table.c.oid),
  2836. # ignore pg_catalog schema
  2837. pg_catalog.pg_namespace.c.nspname != "pg_catalog",
  2838. )
  2839. else:
  2840. query = query.where(pg_catalog.pg_namespace.c.nspname == schema)
  2841. return query
  2842. def _pg_class_relkind_condition(self, relkinds, pg_class_table=None):
  2843. if pg_class_table is None:
  2844. pg_class_table = pg_catalog.pg_class
  2845. # uses the any form instead of in otherwise postgresql complaings
  2846. # that 'IN could not convert type character to "char"'
  2847. return pg_class_table.c.relkind == sql.any_(_array.array(relkinds))
  2848. @lru_cache()
  2849. def _has_table_query(self, schema):
  2850. query = select(pg_catalog.pg_class.c.relname).where(
  2851. pg_catalog.pg_class.c.relname == bindparam("table_name"),
  2852. self._pg_class_relkind_condition(
  2853. pg_catalog.RELKINDS_ALL_TABLE_LIKE
  2854. ),
  2855. )
  2856. return self._pg_class_filter_scope_schema(
  2857. query, schema, scope=ObjectScope.ANY
  2858. )
  2859. @reflection.cache
  2860. def has_table(self, connection, table_name, schema=None, **kw):
  2861. self._ensure_has_table_connection(connection)
  2862. query = self._has_table_query(schema)
  2863. return bool(connection.scalar(query, {"table_name": table_name}))
  2864. @reflection.cache
  2865. def has_sequence(self, connection, sequence_name, schema=None, **kw):
  2866. query = select(pg_catalog.pg_class.c.relname).where(
  2867. pg_catalog.pg_class.c.relkind == "S",
  2868. pg_catalog.pg_class.c.relname == sequence_name,
  2869. )
  2870. query = self._pg_class_filter_scope_schema(
  2871. query, schema, scope=ObjectScope.ANY
  2872. )
  2873. return bool(connection.scalar(query))
  2874. @reflection.cache
  2875. def has_type(self, connection, type_name, schema=None, **kw):
  2876. query = (
  2877. select(pg_catalog.pg_type.c.typname)
  2878. .join(
  2879. pg_catalog.pg_namespace,
  2880. pg_catalog.pg_namespace.c.oid
  2881. == pg_catalog.pg_type.c.typnamespace,
  2882. )
  2883. .where(pg_catalog.pg_type.c.typname == type_name)
  2884. )
  2885. if schema is None:
  2886. query = query.where(
  2887. pg_catalog.pg_type_is_visible(pg_catalog.pg_type.c.oid),
  2888. # ignore pg_catalog schema
  2889. pg_catalog.pg_namespace.c.nspname != "pg_catalog",
  2890. )
  2891. elif schema != "*":
  2892. query = query.where(pg_catalog.pg_namespace.c.nspname == schema)
  2893. return bool(connection.scalar(query))
  2894. def _get_server_version_info(self, connection):
  2895. v = connection.exec_driver_sql("select pg_catalog.version()").scalar()
  2896. m = re.match(
  2897. r".*(?:PostgreSQL|EnterpriseDB) "
  2898. r"(\d+)\.?(\d+)?(?:\.(\d+))?(?:\.\d+)?(?:devel|beta)?",
  2899. v,
  2900. )
  2901. if not m:
  2902. raise AssertionError(
  2903. "Could not determine version from string '%s'" % v
  2904. )
  2905. return tuple([int(x) for x in m.group(1, 2, 3) if x is not None])
  2906. @reflection.cache
  2907. def get_table_oid(self, connection, table_name, schema=None, **kw):
  2908. """Fetch the oid for schema.table_name."""
  2909. query = select(pg_catalog.pg_class.c.oid).where(
  2910. pg_catalog.pg_class.c.relname == table_name,
  2911. self._pg_class_relkind_condition(
  2912. pg_catalog.RELKINDS_ALL_TABLE_LIKE
  2913. ),
  2914. )
  2915. query = self._pg_class_filter_scope_schema(
  2916. query, schema, scope=ObjectScope.ANY
  2917. )
  2918. table_oid = connection.scalar(query)
  2919. if table_oid is None:
  2920. raise exc.NoSuchTableError(
  2921. f"{schema}.{table_name}" if schema else table_name
  2922. )
  2923. return table_oid
  2924. @reflection.cache
  2925. def get_schema_names(self, connection, **kw):
  2926. query = (
  2927. select(pg_catalog.pg_namespace.c.nspname)
  2928. .where(pg_catalog.pg_namespace.c.nspname.not_like("pg_%"))
  2929. .order_by(pg_catalog.pg_namespace.c.nspname)
  2930. )
  2931. return connection.scalars(query).all()
  2932. def _get_relnames_for_relkinds(self, connection, schema, relkinds, scope):
  2933. query = select(pg_catalog.pg_class.c.relname).where(
  2934. self._pg_class_relkind_condition(relkinds)
  2935. )
  2936. query = self._pg_class_filter_scope_schema(query, schema, scope=scope)
  2937. return connection.scalars(query).all()
  2938. @reflection.cache
  2939. def get_table_names(self, connection, schema=None, **kw):
  2940. return self._get_relnames_for_relkinds(
  2941. connection,
  2942. schema,
  2943. pg_catalog.RELKINDS_TABLE_NO_FOREIGN,
  2944. scope=ObjectScope.DEFAULT,
  2945. )
  2946. @reflection.cache
  2947. def get_temp_table_names(self, connection, **kw):
  2948. return self._get_relnames_for_relkinds(
  2949. connection,
  2950. schema=None,
  2951. relkinds=pg_catalog.RELKINDS_TABLE_NO_FOREIGN,
  2952. scope=ObjectScope.TEMPORARY,
  2953. )
  2954. @reflection.cache
  2955. def _get_foreign_table_names(self, connection, schema=None, **kw):
  2956. return self._get_relnames_for_relkinds(
  2957. connection, schema, relkinds=("f",), scope=ObjectScope.ANY
  2958. )
  2959. @reflection.cache
  2960. def get_view_names(self, connection, schema=None, **kw):
  2961. return self._get_relnames_for_relkinds(
  2962. connection,
  2963. schema,
  2964. pg_catalog.RELKINDS_VIEW,
  2965. scope=ObjectScope.DEFAULT,
  2966. )
  2967. @reflection.cache
  2968. def get_materialized_view_names(self, connection, schema=None, **kw):
  2969. return self._get_relnames_for_relkinds(
  2970. connection,
  2971. schema,
  2972. pg_catalog.RELKINDS_MAT_VIEW,
  2973. scope=ObjectScope.DEFAULT,
  2974. )
  2975. @reflection.cache
  2976. def get_temp_view_names(self, connection, schema=None, **kw):
  2977. return self._get_relnames_for_relkinds(
  2978. connection,
  2979. schema,
  2980. # NOTE: do not include temp materialzied views (that do not
  2981. # seem to be a thing at least up to version 14)
  2982. pg_catalog.RELKINDS_VIEW,
  2983. scope=ObjectScope.TEMPORARY,
  2984. )
  2985. @reflection.cache
  2986. def get_sequence_names(self, connection, schema=None, **kw):
  2987. return self._get_relnames_for_relkinds(
  2988. connection, schema, relkinds=("S",), scope=ObjectScope.ANY
  2989. )
  2990. @reflection.cache
  2991. def get_view_definition(self, connection, view_name, schema=None, **kw):
  2992. query = (
  2993. select(pg_catalog.pg_get_viewdef(pg_catalog.pg_class.c.oid))
  2994. .select_from(pg_catalog.pg_class)
  2995. .where(
  2996. pg_catalog.pg_class.c.relname == view_name,
  2997. self._pg_class_relkind_condition(
  2998. pg_catalog.RELKINDS_VIEW + pg_catalog.RELKINDS_MAT_VIEW
  2999. ),
  3000. )
  3001. )
  3002. query = self._pg_class_filter_scope_schema(
  3003. query, schema, scope=ObjectScope.ANY
  3004. )
  3005. res = connection.scalar(query)
  3006. if res is None:
  3007. raise exc.NoSuchTableError(
  3008. f"{schema}.{view_name}" if schema else view_name
  3009. )
  3010. else:
  3011. return res
  3012. def _value_or_raise(self, data, table, schema):
  3013. try:
  3014. return dict(data)[(schema, table)]
  3015. except KeyError:
  3016. raise exc.NoSuchTableError(
  3017. f"{schema}.{table}" if schema else table
  3018. ) from None
  3019. def _prepare_filter_names(self, filter_names):
  3020. if filter_names:
  3021. return True, {"filter_names": filter_names}
  3022. else:
  3023. return False, {}
  3024. def _kind_to_relkinds(self, kind: ObjectKind) -> Tuple[str, ...]:
  3025. if kind is ObjectKind.ANY:
  3026. return pg_catalog.RELKINDS_ALL_TABLE_LIKE
  3027. relkinds = ()
  3028. if ObjectKind.TABLE in kind:
  3029. relkinds += pg_catalog.RELKINDS_TABLE
  3030. if ObjectKind.VIEW in kind:
  3031. relkinds += pg_catalog.RELKINDS_VIEW
  3032. if ObjectKind.MATERIALIZED_VIEW in kind:
  3033. relkinds += pg_catalog.RELKINDS_MAT_VIEW
  3034. return relkinds
  3035. @reflection.cache
  3036. def get_columns(self, connection, table_name, schema=None, **kw):
  3037. data = self.get_multi_columns(
  3038. connection,
  3039. schema=schema,
  3040. filter_names=[table_name],
  3041. scope=ObjectScope.ANY,
  3042. kind=ObjectKind.ANY,
  3043. **kw,
  3044. )
  3045. return self._value_or_raise(data, table_name, schema)
  3046. @lru_cache()
  3047. def _columns_query(self, schema, has_filter_names, scope, kind):
  3048. # NOTE: the query with the default and identity options scalar
  3049. # subquery is faster than trying to use outer joins for them
  3050. generated = (
  3051. pg_catalog.pg_attribute.c.attgenerated.label("generated")
  3052. if self.server_version_info >= (12,)
  3053. else sql.null().label("generated")
  3054. )
  3055. if self.server_version_info >= (10,):
  3056. # join lateral performs worse (~2x slower) than a scalar_subquery
  3057. identity = (
  3058. select(
  3059. sql.func.json_build_object(
  3060. "always",
  3061. pg_catalog.pg_attribute.c.attidentity == "a",
  3062. "start",
  3063. pg_catalog.pg_sequence.c.seqstart,
  3064. "increment",
  3065. pg_catalog.pg_sequence.c.seqincrement,
  3066. "minvalue",
  3067. pg_catalog.pg_sequence.c.seqmin,
  3068. "maxvalue",
  3069. pg_catalog.pg_sequence.c.seqmax,
  3070. "cache",
  3071. pg_catalog.pg_sequence.c.seqcache,
  3072. "cycle",
  3073. pg_catalog.pg_sequence.c.seqcycle,
  3074. type_=sqltypes.JSON(),
  3075. )
  3076. )
  3077. .select_from(pg_catalog.pg_sequence)
  3078. .where(
  3079. # attidentity != '' is required or it will reflect also
  3080. # serial columns as identity.
  3081. pg_catalog.pg_attribute.c.attidentity != "",
  3082. pg_catalog.pg_sequence.c.seqrelid
  3083. == sql.cast(
  3084. sql.cast(
  3085. pg_catalog.pg_get_serial_sequence(
  3086. sql.cast(
  3087. sql.cast(
  3088. pg_catalog.pg_attribute.c.attrelid,
  3089. REGCLASS,
  3090. ),
  3091. TEXT,
  3092. ),
  3093. pg_catalog.pg_attribute.c.attname,
  3094. ),
  3095. REGCLASS,
  3096. ),
  3097. OID,
  3098. ),
  3099. )
  3100. .correlate(pg_catalog.pg_attribute)
  3101. .scalar_subquery()
  3102. .label("identity_options")
  3103. )
  3104. else:
  3105. identity = sql.null().label("identity_options")
  3106. # join lateral performs the same as scalar_subquery here
  3107. default = (
  3108. select(
  3109. pg_catalog.pg_get_expr(
  3110. pg_catalog.pg_attrdef.c.adbin,
  3111. pg_catalog.pg_attrdef.c.adrelid,
  3112. )
  3113. )
  3114. .select_from(pg_catalog.pg_attrdef)
  3115. .where(
  3116. pg_catalog.pg_attrdef.c.adrelid
  3117. == pg_catalog.pg_attribute.c.attrelid,
  3118. pg_catalog.pg_attrdef.c.adnum
  3119. == pg_catalog.pg_attribute.c.attnum,
  3120. pg_catalog.pg_attribute.c.atthasdef,
  3121. )
  3122. .correlate(pg_catalog.pg_attribute)
  3123. .scalar_subquery()
  3124. .label("default")
  3125. )
  3126. # get the name of the collate when it's different from the default one
  3127. collate = sql.case(
  3128. (
  3129. sql.and_(
  3130. pg_catalog.pg_attribute.c.attcollation != 0,
  3131. select(pg_catalog.pg_type.c.typcollation)
  3132. .where(
  3133. pg_catalog.pg_type.c.oid
  3134. == pg_catalog.pg_attribute.c.atttypid,
  3135. )
  3136. .correlate(pg_catalog.pg_attribute)
  3137. .scalar_subquery()
  3138. != pg_catalog.pg_attribute.c.attcollation,
  3139. ),
  3140. select(pg_catalog.pg_collation.c.collname)
  3141. .where(
  3142. pg_catalog.pg_collation.c.oid
  3143. == pg_catalog.pg_attribute.c.attcollation
  3144. )
  3145. .correlate(pg_catalog.pg_attribute)
  3146. .scalar_subquery(),
  3147. ),
  3148. else_=sql.null(),
  3149. ).label("collation")
  3150. relkinds = self._kind_to_relkinds(kind)
  3151. query = (
  3152. select(
  3153. pg_catalog.pg_attribute.c.attname.label("name"),
  3154. pg_catalog.format_type(
  3155. pg_catalog.pg_attribute.c.atttypid,
  3156. pg_catalog.pg_attribute.c.atttypmod,
  3157. ).label("format_type"),
  3158. default,
  3159. pg_catalog.pg_attribute.c.attnotnull.label("not_null"),
  3160. pg_catalog.pg_class.c.relname.label("table_name"),
  3161. pg_catalog.pg_description.c.description.label("comment"),
  3162. generated,
  3163. identity,
  3164. collate,
  3165. )
  3166. .select_from(pg_catalog.pg_class)
  3167. # NOTE: postgresql support table with no user column, meaning
  3168. # there is no row with pg_attribute.attnum > 0. use a left outer
  3169. # join to avoid filtering these tables.
  3170. .outerjoin(
  3171. pg_catalog.pg_attribute,
  3172. sql.and_(
  3173. pg_catalog.pg_class.c.oid
  3174. == pg_catalog.pg_attribute.c.attrelid,
  3175. pg_catalog.pg_attribute.c.attnum > 0,
  3176. ~pg_catalog.pg_attribute.c.attisdropped,
  3177. ),
  3178. )
  3179. .outerjoin(
  3180. pg_catalog.pg_description,
  3181. sql.and_(
  3182. pg_catalog.pg_description.c.objoid
  3183. == pg_catalog.pg_attribute.c.attrelid,
  3184. pg_catalog.pg_description.c.objsubid
  3185. == pg_catalog.pg_attribute.c.attnum,
  3186. ),
  3187. )
  3188. .where(self._pg_class_relkind_condition(relkinds))
  3189. .order_by(
  3190. pg_catalog.pg_class.c.relname, pg_catalog.pg_attribute.c.attnum
  3191. )
  3192. )
  3193. query = self._pg_class_filter_scope_schema(query, schema, scope=scope)
  3194. if has_filter_names:
  3195. query = query.where(
  3196. pg_catalog.pg_class.c.relname.in_(bindparam("filter_names"))
  3197. )
  3198. return query
  3199. def get_multi_columns(
  3200. self, connection, schema, filter_names, scope, kind, **kw
  3201. ):
  3202. has_filter_names, params = self._prepare_filter_names(filter_names)
  3203. query = self._columns_query(schema, has_filter_names, scope, kind)
  3204. rows = connection.execute(query, params).mappings()
  3205. # dictionary with (name, ) if default search path or (schema, name)
  3206. # as keys
  3207. domains = {
  3208. ((d["schema"], d["name"]) if not d["visible"] else (d["name"],)): d
  3209. for d in self._load_domains(
  3210. connection, schema="*", info_cache=kw.get("info_cache")
  3211. )
  3212. }
  3213. # dictionary with (name, ) if default search path or (schema, name)
  3214. # as keys
  3215. enums = dict(
  3216. (
  3217. ((rec["name"],), rec)
  3218. if rec["visible"]
  3219. else ((rec["schema"], rec["name"]), rec)
  3220. )
  3221. for rec in self._load_enums(
  3222. connection, schema="*", info_cache=kw.get("info_cache")
  3223. )
  3224. )
  3225. columns = self._get_columns_info(rows, domains, enums, schema)
  3226. return columns.items()
  3227. _format_type_args_pattern = re.compile(r"\((.*)\)")
  3228. _format_type_args_delim = re.compile(r"\s*,\s*")
  3229. _format_array_spec_pattern = re.compile(r"((?:\[\])*)$")
  3230. def _reflect_type(
  3231. self,
  3232. format_type: Optional[str],
  3233. domains: Dict[str, ReflectedDomain],
  3234. enums: Dict[str, ReflectedEnum],
  3235. type_description: str,
  3236. collation: Optional[str],
  3237. ) -> sqltypes.TypeEngine[Any]:
  3238. """
  3239. Attempts to reconstruct a column type defined in ischema_names based
  3240. on the information available in the format_type.
  3241. If the `format_type` cannot be associated with a known `ischema_names`,
  3242. it is treated as a reference to a known PostgreSQL named `ENUM` or
  3243. `DOMAIN` type.
  3244. """
  3245. type_description = type_description or "unknown type"
  3246. if format_type is None:
  3247. util.warn(
  3248. "PostgreSQL format_type() returned NULL for %s"
  3249. % type_description
  3250. )
  3251. return sqltypes.NULLTYPE
  3252. attype_args_match = self._format_type_args_pattern.search(format_type)
  3253. if attype_args_match and attype_args_match.group(1):
  3254. attype_args = self._format_type_args_delim.split(
  3255. attype_args_match.group(1)
  3256. )
  3257. else:
  3258. attype_args = ()
  3259. match_array_dim = self._format_array_spec_pattern.search(format_type)
  3260. # Each "[]" in array specs corresponds to an array dimension
  3261. array_dim = len(match_array_dim.group(1) or "") // 2
  3262. # Remove all parameters and array specs from format_type to obtain an
  3263. # ischema_name candidate
  3264. attype = self._format_type_args_pattern.sub("", format_type)
  3265. attype = self._format_array_spec_pattern.sub("", attype)
  3266. schema_type = self.ischema_names.get(attype.lower(), None)
  3267. args, kwargs = (), {}
  3268. if attype == "numeric":
  3269. if len(attype_args) == 2:
  3270. precision, scale = map(int, attype_args)
  3271. args = (precision, scale)
  3272. elif attype == "double precision":
  3273. args = (53,)
  3274. elif attype == "integer":
  3275. args = ()
  3276. elif attype in ("timestamp with time zone", "time with time zone"):
  3277. kwargs["timezone"] = True
  3278. if len(attype_args) == 1:
  3279. kwargs["precision"] = int(attype_args[0])
  3280. elif attype in (
  3281. "timestamp without time zone",
  3282. "time without time zone",
  3283. "time",
  3284. ):
  3285. kwargs["timezone"] = False
  3286. if len(attype_args) == 1:
  3287. kwargs["precision"] = int(attype_args[0])
  3288. elif attype == "bit varying":
  3289. kwargs["varying"] = True
  3290. if len(attype_args) == 1:
  3291. charlen = int(attype_args[0])
  3292. args = (charlen,)
  3293. # a domain or enum can start with interval, so be mindful of that.
  3294. elif attype == "interval" or attype.startswith("interval "):
  3295. schema_type = INTERVAL
  3296. field_match = re.match(r"interval (.+)", attype)
  3297. if field_match:
  3298. kwargs["fields"] = field_match.group(1)
  3299. if len(attype_args) == 1:
  3300. kwargs["precision"] = int(attype_args[0])
  3301. else:
  3302. enum_or_domain_key = tuple(util.quoted_token_parser(attype))
  3303. if enum_or_domain_key in enums:
  3304. schema_type = ENUM
  3305. enum = enums[enum_or_domain_key]
  3306. kwargs["name"] = enum["name"]
  3307. if not enum["visible"]:
  3308. kwargs["schema"] = enum["schema"]
  3309. args = tuple(enum["labels"])
  3310. elif enum_or_domain_key in domains:
  3311. schema_type = DOMAIN
  3312. domain = domains[enum_or_domain_key]
  3313. data_type = self._reflect_type(
  3314. domain["type"],
  3315. domains,
  3316. enums,
  3317. type_description="DOMAIN '%s'" % domain["name"],
  3318. collation=domain["collation"],
  3319. )
  3320. args = (domain["name"], data_type)
  3321. kwargs["collation"] = domain["collation"]
  3322. kwargs["default"] = domain["default"]
  3323. kwargs["not_null"] = not domain["nullable"]
  3324. kwargs["create_type"] = False
  3325. if domain["constraints"]:
  3326. # We only support a single constraint
  3327. check_constraint = domain["constraints"][0]
  3328. kwargs["constraint_name"] = check_constraint["name"]
  3329. kwargs["check"] = check_constraint["check"]
  3330. if not domain["visible"]:
  3331. kwargs["schema"] = domain["schema"]
  3332. else:
  3333. try:
  3334. charlen = int(attype_args[0])
  3335. args = (charlen, *attype_args[1:])
  3336. except (ValueError, IndexError):
  3337. args = attype_args
  3338. if not schema_type:
  3339. util.warn(
  3340. "Did not recognize type '%s' of %s"
  3341. % (attype, type_description)
  3342. )
  3343. return sqltypes.NULLTYPE
  3344. if collation is not None:
  3345. kwargs["collation"] = collation
  3346. data_type = schema_type(*args, **kwargs)
  3347. if array_dim >= 1:
  3348. # postgres does not preserve dimensionality or size of array types.
  3349. data_type = _array.ARRAY(data_type)
  3350. return data_type
  3351. def _get_columns_info(self, rows, domains, enums, schema):
  3352. columns = defaultdict(list)
  3353. for row_dict in rows:
  3354. # ensure that each table has an entry, even if it has no columns
  3355. if row_dict["name"] is None:
  3356. columns[(schema, row_dict["table_name"])] = (
  3357. ReflectionDefaults.columns()
  3358. )
  3359. continue
  3360. table_cols = columns[(schema, row_dict["table_name"])]
  3361. collation = row_dict["collation"]
  3362. coltype = self._reflect_type(
  3363. row_dict["format_type"],
  3364. domains,
  3365. enums,
  3366. type_description="column '%s'" % row_dict["name"],
  3367. collation=collation,
  3368. )
  3369. default = row_dict["default"]
  3370. name = row_dict["name"]
  3371. generated = row_dict["generated"]
  3372. nullable = not row_dict["not_null"]
  3373. if isinstance(coltype, DOMAIN):
  3374. if not default:
  3375. # domain can override the default value but
  3376. # can't set it to None
  3377. if coltype.default is not None:
  3378. default = coltype.default
  3379. nullable = nullable and not coltype.not_null
  3380. identity = row_dict["identity_options"]
  3381. # If a zero byte or blank string depending on driver (is also
  3382. # absent for older PG versions), then not a generated column.
  3383. # Otherwise, s = stored. (Other values might be added in the
  3384. # future.)
  3385. if generated not in (None, "", b"\x00"):
  3386. computed = dict(
  3387. sqltext=default, persisted=generated in ("s", b"s")
  3388. )
  3389. default = None
  3390. else:
  3391. computed = None
  3392. # adjust the default value
  3393. autoincrement = False
  3394. if default is not None:
  3395. match = re.search(r"""(nextval\(')([^']+)('.*$)""", default)
  3396. if match is not None:
  3397. if issubclass(coltype._type_affinity, sqltypes.Integer):
  3398. autoincrement = True
  3399. # the default is related to a Sequence
  3400. if "." not in match.group(2) and schema is not None:
  3401. # unconditionally quote the schema name. this could
  3402. # later be enhanced to obey quoting rules /
  3403. # "quote schema"
  3404. default = (
  3405. match.group(1)
  3406. + ('"%s"' % schema)
  3407. + "."
  3408. + match.group(2)
  3409. + match.group(3)
  3410. )
  3411. column_info = {
  3412. "name": name,
  3413. "type": coltype,
  3414. "nullable": nullable,
  3415. "default": default,
  3416. "autoincrement": autoincrement or identity is not None,
  3417. "comment": row_dict["comment"],
  3418. }
  3419. if computed is not None:
  3420. column_info["computed"] = computed
  3421. if identity is not None:
  3422. column_info["identity"] = identity
  3423. table_cols.append(column_info)
  3424. return columns
  3425. @lru_cache()
  3426. def _table_oids_query(self, schema, has_filter_names, scope, kind):
  3427. relkinds = self._kind_to_relkinds(kind)
  3428. oid_q = select(
  3429. pg_catalog.pg_class.c.oid, pg_catalog.pg_class.c.relname
  3430. ).where(self._pg_class_relkind_condition(relkinds))
  3431. oid_q = self._pg_class_filter_scope_schema(oid_q, schema, scope=scope)
  3432. if has_filter_names:
  3433. oid_q = oid_q.where(
  3434. pg_catalog.pg_class.c.relname.in_(bindparam("filter_names"))
  3435. )
  3436. return oid_q
  3437. @reflection.flexi_cache(
  3438. ("schema", InternalTraversal.dp_string),
  3439. ("filter_names", InternalTraversal.dp_string_list),
  3440. ("kind", InternalTraversal.dp_plain_obj),
  3441. ("scope", InternalTraversal.dp_plain_obj),
  3442. )
  3443. def _get_table_oids(
  3444. self, connection, schema, filter_names, scope, kind, **kw
  3445. ):
  3446. has_filter_names, params = self._prepare_filter_names(filter_names)
  3447. oid_q = self._table_oids_query(schema, has_filter_names, scope, kind)
  3448. result = connection.execute(oid_q, params)
  3449. return result.all()
  3450. @util.memoized_property
  3451. def _constraint_query(self):
  3452. if self.server_version_info >= (11, 0):
  3453. indnkeyatts = pg_catalog.pg_index.c.indnkeyatts
  3454. else:
  3455. indnkeyatts = pg_catalog.pg_index.c.indnatts.label("indnkeyatts")
  3456. if self.server_version_info >= (15,):
  3457. indnullsnotdistinct = pg_catalog.pg_index.c.indnullsnotdistinct
  3458. else:
  3459. indnullsnotdistinct = sql.false().label("indnullsnotdistinct")
  3460. con_sq = (
  3461. select(
  3462. pg_catalog.pg_constraint.c.conrelid,
  3463. pg_catalog.pg_constraint.c.conname,
  3464. sql.func.unnest(pg_catalog.pg_index.c.indkey).label("attnum"),
  3465. sql.func.generate_subscripts(
  3466. pg_catalog.pg_index.c.indkey, 1
  3467. ).label("ord"),
  3468. indnkeyatts,
  3469. indnullsnotdistinct,
  3470. pg_catalog.pg_description.c.description,
  3471. )
  3472. .join(
  3473. pg_catalog.pg_index,
  3474. pg_catalog.pg_constraint.c.conindid
  3475. == pg_catalog.pg_index.c.indexrelid,
  3476. )
  3477. .outerjoin(
  3478. pg_catalog.pg_description,
  3479. pg_catalog.pg_description.c.objoid
  3480. == pg_catalog.pg_constraint.c.oid,
  3481. )
  3482. .where(
  3483. pg_catalog.pg_constraint.c.contype == bindparam("contype"),
  3484. pg_catalog.pg_constraint.c.conrelid.in_(bindparam("oids")),
  3485. # NOTE: filtering also on pg_index.indrelid for oids does
  3486. # not seem to have a performance effect, but it may be an
  3487. # option if perf problems are reported
  3488. )
  3489. .subquery("con")
  3490. )
  3491. attr_sq = (
  3492. select(
  3493. con_sq.c.conrelid,
  3494. con_sq.c.conname,
  3495. con_sq.c.description,
  3496. con_sq.c.ord,
  3497. con_sq.c.indnkeyatts,
  3498. con_sq.c.indnullsnotdistinct,
  3499. pg_catalog.pg_attribute.c.attname,
  3500. )
  3501. .select_from(pg_catalog.pg_attribute)
  3502. .join(
  3503. con_sq,
  3504. sql.and_(
  3505. pg_catalog.pg_attribute.c.attnum == con_sq.c.attnum,
  3506. pg_catalog.pg_attribute.c.attrelid == con_sq.c.conrelid,
  3507. ),
  3508. )
  3509. .where(
  3510. # NOTE: restate the condition here, since pg15 otherwise
  3511. # seems to get confused on pscopg2 sometimes, doing
  3512. # a sequential scan of pg_attribute.
  3513. # The condition in the con_sq subquery is not actually needed
  3514. # in pg15, but it may be needed in older versions. Keeping it
  3515. # does not seems to have any impact in any case.
  3516. con_sq.c.conrelid.in_(bindparam("oids"))
  3517. )
  3518. .subquery("attr")
  3519. )
  3520. return (
  3521. select(
  3522. attr_sq.c.conrelid,
  3523. sql.func.array_agg(
  3524. # NOTE: cast since some postgresql derivatives may
  3525. # not support array_agg on the name type
  3526. aggregate_order_by(
  3527. attr_sq.c.attname.cast(TEXT), attr_sq.c.ord
  3528. )
  3529. ).label("cols"),
  3530. attr_sq.c.conname,
  3531. sql.func.min(attr_sq.c.description).label("description"),
  3532. sql.func.min(attr_sq.c.indnkeyatts).label("indnkeyatts"),
  3533. sql.func.bool_and(attr_sq.c.indnullsnotdistinct).label(
  3534. "indnullsnotdistinct"
  3535. ),
  3536. )
  3537. .group_by(attr_sq.c.conrelid, attr_sq.c.conname)
  3538. .order_by(attr_sq.c.conrelid, attr_sq.c.conname)
  3539. )
  3540. def _reflect_constraint(
  3541. self, connection, contype, schema, filter_names, scope, kind, **kw
  3542. ):
  3543. # used to reflect primary and unique constraint
  3544. table_oids = self._get_table_oids(
  3545. connection, schema, filter_names, scope, kind, **kw
  3546. )
  3547. batches = list(table_oids)
  3548. is_unique = contype == "u"
  3549. while batches:
  3550. batch = batches[0:3000]
  3551. batches[0:3000] = []
  3552. result = connection.execute(
  3553. self._constraint_query,
  3554. {"oids": [r[0] for r in batch], "contype": contype},
  3555. ).mappings()
  3556. result_by_oid = defaultdict(list)
  3557. for row_dict in result:
  3558. result_by_oid[row_dict["conrelid"]].append(row_dict)
  3559. for oid, tablename in batch:
  3560. for_oid = result_by_oid.get(oid, ())
  3561. if for_oid:
  3562. for row in for_oid:
  3563. # See note in get_multi_indexes
  3564. all_cols = row["cols"]
  3565. indnkeyatts = row["indnkeyatts"]
  3566. if len(all_cols) > indnkeyatts:
  3567. inc_cols = all_cols[indnkeyatts:]
  3568. cst_cols = all_cols[:indnkeyatts]
  3569. else:
  3570. inc_cols = []
  3571. cst_cols = all_cols
  3572. opts = {}
  3573. if self.server_version_info >= (11,):
  3574. opts["postgresql_include"] = inc_cols
  3575. if is_unique:
  3576. opts["postgresql_nulls_not_distinct"] = row[
  3577. "indnullsnotdistinct"
  3578. ]
  3579. yield (
  3580. tablename,
  3581. cst_cols,
  3582. row["conname"],
  3583. row["description"],
  3584. opts,
  3585. )
  3586. else:
  3587. yield tablename, None, None, None, None
  3588. @reflection.cache
  3589. def get_pk_constraint(self, connection, table_name, schema=None, **kw):
  3590. data = self.get_multi_pk_constraint(
  3591. connection,
  3592. schema=schema,
  3593. filter_names=[table_name],
  3594. scope=ObjectScope.ANY,
  3595. kind=ObjectKind.ANY,
  3596. **kw,
  3597. )
  3598. return self._value_or_raise(data, table_name, schema)
  3599. def get_multi_pk_constraint(
  3600. self, connection, schema, filter_names, scope, kind, **kw
  3601. ):
  3602. result = self._reflect_constraint(
  3603. connection, "p", schema, filter_names, scope, kind, **kw
  3604. )
  3605. # only a single pk can be present for each table. Return an entry
  3606. # even if a table has no primary key
  3607. default = ReflectionDefaults.pk_constraint
  3608. def pk_constraint(pk_name, cols, comment, opts):
  3609. info = {
  3610. "constrained_columns": cols,
  3611. "name": pk_name,
  3612. "comment": comment,
  3613. }
  3614. if opts:
  3615. info["dialect_options"] = opts
  3616. return info
  3617. return (
  3618. (
  3619. (schema, table_name),
  3620. (
  3621. pk_constraint(pk_name, cols, comment, opts)
  3622. if pk_name is not None
  3623. else default()
  3624. ),
  3625. )
  3626. for table_name, cols, pk_name, comment, opts in result
  3627. )
  3628. @reflection.cache
  3629. def get_foreign_keys(
  3630. self,
  3631. connection,
  3632. table_name,
  3633. schema=None,
  3634. postgresql_ignore_search_path=False,
  3635. **kw,
  3636. ):
  3637. data = self.get_multi_foreign_keys(
  3638. connection,
  3639. schema=schema,
  3640. filter_names=[table_name],
  3641. postgresql_ignore_search_path=postgresql_ignore_search_path,
  3642. scope=ObjectScope.ANY,
  3643. kind=ObjectKind.ANY,
  3644. **kw,
  3645. )
  3646. return self._value_or_raise(data, table_name, schema)
  3647. @lru_cache()
  3648. def _foreing_key_query(self, schema, has_filter_names, scope, kind):
  3649. pg_class_ref = pg_catalog.pg_class.alias("cls_ref")
  3650. pg_namespace_ref = pg_catalog.pg_namespace.alias("nsp_ref")
  3651. relkinds = self._kind_to_relkinds(kind)
  3652. query = (
  3653. select(
  3654. pg_catalog.pg_class.c.relname,
  3655. pg_catalog.pg_constraint.c.conname,
  3656. # NOTE: avoid calling pg_get_constraintdef when not needed
  3657. # to speed up the query
  3658. sql.case(
  3659. (
  3660. pg_catalog.pg_constraint.c.oid.is_not(None),
  3661. pg_catalog.pg_get_constraintdef(
  3662. pg_catalog.pg_constraint.c.oid, True
  3663. ),
  3664. ),
  3665. else_=None,
  3666. ),
  3667. pg_namespace_ref.c.nspname,
  3668. pg_catalog.pg_description.c.description,
  3669. )
  3670. .select_from(pg_catalog.pg_class)
  3671. .outerjoin(
  3672. pg_catalog.pg_constraint,
  3673. sql.and_(
  3674. pg_catalog.pg_class.c.oid
  3675. == pg_catalog.pg_constraint.c.conrelid,
  3676. pg_catalog.pg_constraint.c.contype == "f",
  3677. ),
  3678. )
  3679. .outerjoin(
  3680. pg_class_ref,
  3681. pg_class_ref.c.oid == pg_catalog.pg_constraint.c.confrelid,
  3682. )
  3683. .outerjoin(
  3684. pg_namespace_ref,
  3685. pg_class_ref.c.relnamespace == pg_namespace_ref.c.oid,
  3686. )
  3687. .outerjoin(
  3688. pg_catalog.pg_description,
  3689. pg_catalog.pg_description.c.objoid
  3690. == pg_catalog.pg_constraint.c.oid,
  3691. )
  3692. .order_by(
  3693. pg_catalog.pg_class.c.relname,
  3694. pg_catalog.pg_constraint.c.conname,
  3695. )
  3696. .where(self._pg_class_relkind_condition(relkinds))
  3697. )
  3698. query = self._pg_class_filter_scope_schema(query, schema, scope)
  3699. if has_filter_names:
  3700. query = query.where(
  3701. pg_catalog.pg_class.c.relname.in_(bindparam("filter_names"))
  3702. )
  3703. return query
  3704. @util.memoized_property
  3705. def _fk_regex_pattern(self):
  3706. # optionally quoted token
  3707. qtoken = r'(?:"[^"]+"|[\w]+?)'
  3708. # https://www.postgresql.org/docs/current/static/sql-createtable.html
  3709. return re.compile(
  3710. r"FOREIGN KEY \((.*?)\) "
  3711. rf"REFERENCES (?:({qtoken})\.)?({qtoken})\(((?:{qtoken}(?: *, *)?)+)\)" # noqa: E501
  3712. r"[\s]?(MATCH (FULL|PARTIAL|SIMPLE)+)?"
  3713. r"[\s]?(?:ON (UPDATE|DELETE) "
  3714. r"(CASCADE|RESTRICT|NO ACTION|"
  3715. r"SET (?:NULL|DEFAULT)(?:\s\(.+\))?)+)?"
  3716. r"[\s]?(?:ON (UPDATE|DELETE) "
  3717. r"(CASCADE|RESTRICT|NO ACTION|"
  3718. r"SET (?:NULL|DEFAULT)(?:\s\(.+\))?)+)?"
  3719. r"[\s]?(DEFERRABLE|NOT DEFERRABLE)?"
  3720. r"[\s]?(INITIALLY (DEFERRED|IMMEDIATE)+)?"
  3721. )
  3722. def _parse_fk(self, condef):
  3723. FK_REGEX = self._fk_regex_pattern
  3724. m = re.search(FK_REGEX, condef).groups()
  3725. (
  3726. constrained_columns,
  3727. referred_schema,
  3728. referred_table,
  3729. referred_columns,
  3730. _,
  3731. match,
  3732. upddelkey1,
  3733. upddelval1,
  3734. upddelkey2,
  3735. upddelval2,
  3736. deferrable,
  3737. _,
  3738. initially,
  3739. ) = m
  3740. onupdate = (
  3741. upddelval1
  3742. if upddelkey1 == "UPDATE"
  3743. else upddelval2 if upddelkey2 == "UPDATE" else None
  3744. )
  3745. ondelete = (
  3746. upddelval1
  3747. if upddelkey1 == "DELETE"
  3748. else upddelval2 if upddelkey2 == "DELETE" else None
  3749. )
  3750. return (
  3751. constrained_columns,
  3752. referred_schema,
  3753. referred_table,
  3754. referred_columns,
  3755. match,
  3756. onupdate,
  3757. ondelete,
  3758. deferrable,
  3759. initially,
  3760. )
  3761. def get_multi_foreign_keys(
  3762. self,
  3763. connection,
  3764. schema,
  3765. filter_names,
  3766. scope,
  3767. kind,
  3768. postgresql_ignore_search_path=False,
  3769. **kw,
  3770. ):
  3771. preparer = self.identifier_preparer
  3772. has_filter_names, params = self._prepare_filter_names(filter_names)
  3773. query = self._foreing_key_query(schema, has_filter_names, scope, kind)
  3774. result = connection.execute(query, params)
  3775. fkeys = defaultdict(list)
  3776. default = ReflectionDefaults.foreign_keys
  3777. for table_name, conname, condef, conschema, comment in result:
  3778. # ensure that each table has an entry, even if it has
  3779. # no foreign keys
  3780. if conname is None:
  3781. fkeys[(schema, table_name)] = default()
  3782. continue
  3783. table_fks = fkeys[(schema, table_name)]
  3784. (
  3785. constrained_columns,
  3786. referred_schema,
  3787. referred_table,
  3788. referred_columns,
  3789. match,
  3790. onupdate,
  3791. ondelete,
  3792. deferrable,
  3793. initially,
  3794. ) = self._parse_fk(condef)
  3795. if deferrable is not None:
  3796. deferrable = True if deferrable == "DEFERRABLE" else False
  3797. constrained_columns = [
  3798. preparer._unquote_identifier(x)
  3799. for x in re.split(r"\s*,\s*", constrained_columns)
  3800. ]
  3801. if postgresql_ignore_search_path:
  3802. # when ignoring search path, we use the actual schema
  3803. # provided it isn't the "default" schema
  3804. if conschema != self.default_schema_name:
  3805. referred_schema = conschema
  3806. else:
  3807. referred_schema = schema
  3808. elif referred_schema:
  3809. # referred_schema is the schema that we regexp'ed from
  3810. # pg_get_constraintdef(). If the schema is in the search
  3811. # path, pg_get_constraintdef() will give us None.
  3812. referred_schema = preparer._unquote_identifier(referred_schema)
  3813. elif schema is not None and schema == conschema:
  3814. # If the actual schema matches the schema of the table
  3815. # we're reflecting, then we will use that.
  3816. referred_schema = schema
  3817. referred_table = preparer._unquote_identifier(referred_table)
  3818. referred_columns = [
  3819. preparer._unquote_identifier(x)
  3820. for x in re.split(r"\s*,\s", referred_columns)
  3821. ]
  3822. options = {
  3823. k: v
  3824. for k, v in [
  3825. ("onupdate", onupdate),
  3826. ("ondelete", ondelete),
  3827. ("initially", initially),
  3828. ("deferrable", deferrable),
  3829. ("match", match),
  3830. ]
  3831. if v is not None and v != "NO ACTION"
  3832. }
  3833. fkey_d = {
  3834. "name": conname,
  3835. "constrained_columns": constrained_columns,
  3836. "referred_schema": referred_schema,
  3837. "referred_table": referred_table,
  3838. "referred_columns": referred_columns,
  3839. "options": options,
  3840. "comment": comment,
  3841. }
  3842. table_fks.append(fkey_d)
  3843. return fkeys.items()
  3844. @reflection.cache
  3845. def get_indexes(self, connection, table_name, schema=None, **kw):
  3846. data = self.get_multi_indexes(
  3847. connection,
  3848. schema=schema,
  3849. filter_names=[table_name],
  3850. scope=ObjectScope.ANY,
  3851. kind=ObjectKind.ANY,
  3852. **kw,
  3853. )
  3854. return self._value_or_raise(data, table_name, schema)
  3855. @util.memoized_property
  3856. def _index_query(self):
  3857. # NOTE: pg_index is used as from two times to improve performance,
  3858. # since extraing all the index information from `idx_sq` to avoid
  3859. # the second pg_index use leads to a worse performing query in
  3860. # particular when querying for a single table (as of pg 17)
  3861. # NOTE: repeating oids clause improve query performance
  3862. # subquery to get the columns
  3863. idx_sq = (
  3864. select(
  3865. pg_catalog.pg_index.c.indexrelid,
  3866. pg_catalog.pg_index.c.indrelid,
  3867. sql.func.unnest(pg_catalog.pg_index.c.indkey).label("attnum"),
  3868. sql.func.unnest(pg_catalog.pg_index.c.indclass).label(
  3869. "att_opclass"
  3870. ),
  3871. sql.func.generate_subscripts(
  3872. pg_catalog.pg_index.c.indkey, 1
  3873. ).label("ord"),
  3874. )
  3875. .where(
  3876. ~pg_catalog.pg_index.c.indisprimary,
  3877. pg_catalog.pg_index.c.indrelid.in_(bindparam("oids")),
  3878. )
  3879. .subquery("idx")
  3880. )
  3881. attr_sq = (
  3882. select(
  3883. idx_sq.c.indexrelid,
  3884. idx_sq.c.indrelid,
  3885. idx_sq.c.ord,
  3886. # NOTE: always using pg_get_indexdef is too slow so just
  3887. # invoke when the element is an expression
  3888. sql.case(
  3889. (
  3890. idx_sq.c.attnum == 0,
  3891. pg_catalog.pg_get_indexdef(
  3892. idx_sq.c.indexrelid, idx_sq.c.ord + 1, True
  3893. ),
  3894. ),
  3895. # NOTE: need to cast this since attname is of type "name"
  3896. # that's limited to 63 bytes, while pg_get_indexdef
  3897. # returns "text" so its output may get cut
  3898. else_=pg_catalog.pg_attribute.c.attname.cast(TEXT),
  3899. ).label("element"),
  3900. (idx_sq.c.attnum == 0).label("is_expr"),
  3901. pg_catalog.pg_opclass.c.opcname,
  3902. pg_catalog.pg_opclass.c.opcdefault,
  3903. )
  3904. .select_from(idx_sq)
  3905. .outerjoin(
  3906. # do not remove rows where idx_sq.c.attnum is 0
  3907. pg_catalog.pg_attribute,
  3908. sql.and_(
  3909. pg_catalog.pg_attribute.c.attnum == idx_sq.c.attnum,
  3910. pg_catalog.pg_attribute.c.attrelid == idx_sq.c.indrelid,
  3911. ),
  3912. )
  3913. .outerjoin(
  3914. pg_catalog.pg_opclass,
  3915. pg_catalog.pg_opclass.c.oid == idx_sq.c.att_opclass,
  3916. )
  3917. .where(idx_sq.c.indrelid.in_(bindparam("oids")))
  3918. .subquery("idx_attr")
  3919. )
  3920. cols_sq = (
  3921. select(
  3922. attr_sq.c.indexrelid,
  3923. sql.func.min(attr_sq.c.indrelid),
  3924. sql.func.array_agg(
  3925. aggregate_order_by(attr_sq.c.element, attr_sq.c.ord)
  3926. ).label("elements"),
  3927. sql.func.array_agg(
  3928. aggregate_order_by(attr_sq.c.is_expr, attr_sq.c.ord)
  3929. ).label("elements_is_expr"),
  3930. sql.func.array_agg(
  3931. aggregate_order_by(attr_sq.c.opcname, attr_sq.c.ord)
  3932. ).label("elements_opclass"),
  3933. sql.func.array_agg(
  3934. aggregate_order_by(attr_sq.c.opcdefault, attr_sq.c.ord)
  3935. ).label("elements_opdefault"),
  3936. )
  3937. .group_by(attr_sq.c.indexrelid)
  3938. .subquery("idx_cols")
  3939. )
  3940. if self.server_version_info >= (11, 0):
  3941. indnkeyatts = pg_catalog.pg_index.c.indnkeyatts
  3942. else:
  3943. indnkeyatts = pg_catalog.pg_index.c.indnatts.label("indnkeyatts")
  3944. if self.server_version_info >= (15,):
  3945. nulls_not_distinct = pg_catalog.pg_index.c.indnullsnotdistinct
  3946. else:
  3947. nulls_not_distinct = sql.false().label("indnullsnotdistinct")
  3948. return (
  3949. select(
  3950. pg_catalog.pg_index.c.indrelid,
  3951. pg_catalog.pg_class.c.relname,
  3952. pg_catalog.pg_index.c.indisunique,
  3953. pg_catalog.pg_constraint.c.conrelid.is_not(None).label(
  3954. "has_constraint"
  3955. ),
  3956. pg_catalog.pg_index.c.indoption,
  3957. pg_catalog.pg_class.c.reloptions,
  3958. pg_catalog.pg_am.c.amname,
  3959. # NOTE: pg_get_expr is very fast so this case has almost no
  3960. # performance impact
  3961. sql.case(
  3962. (
  3963. pg_catalog.pg_index.c.indpred.is_not(None),
  3964. pg_catalog.pg_get_expr(
  3965. pg_catalog.pg_index.c.indpred,
  3966. pg_catalog.pg_index.c.indrelid,
  3967. ),
  3968. ),
  3969. else_=None,
  3970. ).label("filter_definition"),
  3971. indnkeyatts,
  3972. nulls_not_distinct,
  3973. cols_sq.c.elements,
  3974. cols_sq.c.elements_is_expr,
  3975. cols_sq.c.elements_opclass,
  3976. cols_sq.c.elements_opdefault,
  3977. )
  3978. .select_from(pg_catalog.pg_index)
  3979. .where(
  3980. pg_catalog.pg_index.c.indrelid.in_(bindparam("oids")),
  3981. ~pg_catalog.pg_index.c.indisprimary,
  3982. )
  3983. .join(
  3984. pg_catalog.pg_class,
  3985. pg_catalog.pg_index.c.indexrelid == pg_catalog.pg_class.c.oid,
  3986. )
  3987. .join(
  3988. pg_catalog.pg_am,
  3989. pg_catalog.pg_class.c.relam == pg_catalog.pg_am.c.oid,
  3990. )
  3991. .outerjoin(
  3992. cols_sq,
  3993. pg_catalog.pg_index.c.indexrelid == cols_sq.c.indexrelid,
  3994. )
  3995. .outerjoin(
  3996. pg_catalog.pg_constraint,
  3997. sql.and_(
  3998. pg_catalog.pg_index.c.indrelid
  3999. == pg_catalog.pg_constraint.c.conrelid,
  4000. pg_catalog.pg_index.c.indexrelid
  4001. == pg_catalog.pg_constraint.c.conindid,
  4002. pg_catalog.pg_constraint.c.contype
  4003. == sql.any_(_array.array(("p", "u", "x"))),
  4004. ),
  4005. )
  4006. .order_by(
  4007. pg_catalog.pg_index.c.indrelid, pg_catalog.pg_class.c.relname
  4008. )
  4009. )
  4010. def get_multi_indexes(
  4011. self, connection, schema, filter_names, scope, kind, **kw
  4012. ):
  4013. table_oids = self._get_table_oids(
  4014. connection, schema, filter_names, scope, kind, **kw
  4015. )
  4016. indexes = defaultdict(list)
  4017. default = ReflectionDefaults.indexes
  4018. batches = list(table_oids)
  4019. while batches:
  4020. batch = batches[0:3000]
  4021. batches[0:3000] = []
  4022. result = connection.execute(
  4023. self._index_query, {"oids": [r[0] for r in batch]}
  4024. ).mappings()
  4025. result_by_oid = defaultdict(list)
  4026. for row_dict in result:
  4027. result_by_oid[row_dict["indrelid"]].append(row_dict)
  4028. for oid, table_name in batch:
  4029. if oid not in result_by_oid:
  4030. # ensure that each table has an entry, even if reflection
  4031. # is skipped because not supported
  4032. indexes[(schema, table_name)] = default()
  4033. continue
  4034. for row in result_by_oid[oid]:
  4035. index_name = row["relname"]
  4036. table_indexes = indexes[(schema, table_name)]
  4037. all_elements = row["elements"]
  4038. all_elements_is_expr = row["elements_is_expr"]
  4039. all_elements_opclass = row["elements_opclass"]
  4040. all_elements_opdefault = row["elements_opdefault"]
  4041. indnkeyatts = row["indnkeyatts"]
  4042. # "The number of key columns in the index, not counting any
  4043. # included columns, which are merely stored and do not
  4044. # participate in the index semantics"
  4045. if len(all_elements) > indnkeyatts:
  4046. # this is a "covering index" which has INCLUDE columns
  4047. # as well as regular index columns
  4048. inc_cols = all_elements[indnkeyatts:]
  4049. idx_elements = all_elements[:indnkeyatts]
  4050. idx_elements_is_expr = all_elements_is_expr[
  4051. :indnkeyatts
  4052. ]
  4053. # postgresql does not support expression on included
  4054. # columns as of v14: "ERROR: expressions are not
  4055. # supported in included columns".
  4056. assert all(
  4057. not is_expr
  4058. for is_expr in all_elements_is_expr[indnkeyatts:]
  4059. )
  4060. idx_elements_opclass = all_elements_opclass[
  4061. :indnkeyatts
  4062. ]
  4063. idx_elements_opdefault = all_elements_opdefault[
  4064. :indnkeyatts
  4065. ]
  4066. else:
  4067. idx_elements = all_elements
  4068. idx_elements_is_expr = all_elements_is_expr
  4069. inc_cols = []
  4070. idx_elements_opclass = all_elements_opclass
  4071. idx_elements_opdefault = all_elements_opdefault
  4072. index = {"name": index_name, "unique": row["indisunique"]}
  4073. if any(idx_elements_is_expr):
  4074. index["column_names"] = [
  4075. None if is_expr else expr
  4076. for expr, is_expr in zip(
  4077. idx_elements, idx_elements_is_expr
  4078. )
  4079. ]
  4080. index["expressions"] = idx_elements
  4081. else:
  4082. index["column_names"] = idx_elements
  4083. dialect_options = {}
  4084. if not all(idx_elements_opdefault):
  4085. dialect_options["postgresql_ops"] = {
  4086. name: opclass
  4087. for name, opclass, is_default in zip(
  4088. idx_elements,
  4089. idx_elements_opclass,
  4090. idx_elements_opdefault,
  4091. )
  4092. if not is_default
  4093. }
  4094. sorting = {}
  4095. for col_index, col_flags in enumerate(row["indoption"]):
  4096. col_sorting = ()
  4097. # try to set flags only if they differ from PG
  4098. # defaults...
  4099. if col_flags & 0x01:
  4100. col_sorting += ("desc",)
  4101. if not (col_flags & 0x02):
  4102. col_sorting += ("nulls_last",)
  4103. else:
  4104. if col_flags & 0x02:
  4105. col_sorting += ("nulls_first",)
  4106. if col_sorting:
  4107. sorting[idx_elements[col_index]] = col_sorting
  4108. if sorting:
  4109. index["column_sorting"] = sorting
  4110. if row["has_constraint"]:
  4111. index["duplicates_constraint"] = index_name
  4112. if row["reloptions"]:
  4113. dialect_options["postgresql_with"] = dict(
  4114. [
  4115. option.split("=", 1)
  4116. for option in row["reloptions"]
  4117. ]
  4118. )
  4119. # it *might* be nice to include that this is 'btree' in the
  4120. # reflection info. But we don't want an Index object
  4121. # to have a ``postgresql_using`` in it that is just the
  4122. # default, so for the moment leaving this out.
  4123. amname = row["amname"]
  4124. if amname != "btree":
  4125. dialect_options["postgresql_using"] = row["amname"]
  4126. if row["filter_definition"]:
  4127. dialect_options["postgresql_where"] = row[
  4128. "filter_definition"
  4129. ]
  4130. if self.server_version_info >= (11,):
  4131. # NOTE: this is legacy, this is part of
  4132. # dialect_options now as of #7382
  4133. index["include_columns"] = inc_cols
  4134. dialect_options["postgresql_include"] = inc_cols
  4135. if row["indnullsnotdistinct"]:
  4136. # the default is False, so ignore it.
  4137. dialect_options["postgresql_nulls_not_distinct"] = row[
  4138. "indnullsnotdistinct"
  4139. ]
  4140. if dialect_options:
  4141. index["dialect_options"] = dialect_options
  4142. table_indexes.append(index)
  4143. return indexes.items()
  4144. @reflection.cache
  4145. def get_unique_constraints(
  4146. self, connection, table_name, schema=None, **kw
  4147. ):
  4148. data = self.get_multi_unique_constraints(
  4149. connection,
  4150. schema=schema,
  4151. filter_names=[table_name],
  4152. scope=ObjectScope.ANY,
  4153. kind=ObjectKind.ANY,
  4154. **kw,
  4155. )
  4156. return self._value_or_raise(data, table_name, schema)
  4157. def get_multi_unique_constraints(
  4158. self,
  4159. connection,
  4160. schema,
  4161. filter_names,
  4162. scope,
  4163. kind,
  4164. **kw,
  4165. ):
  4166. result = self._reflect_constraint(
  4167. connection, "u", schema, filter_names, scope, kind, **kw
  4168. )
  4169. # each table can have multiple unique constraints
  4170. uniques = defaultdict(list)
  4171. default = ReflectionDefaults.unique_constraints
  4172. for table_name, cols, con_name, comment, options in result:
  4173. # ensure a list is created for each table. leave it empty if
  4174. # the table has no unique constraint
  4175. if con_name is None:
  4176. uniques[(schema, table_name)] = default()
  4177. continue
  4178. uc_dict = {
  4179. "column_names": cols,
  4180. "name": con_name,
  4181. "comment": comment,
  4182. }
  4183. if options:
  4184. uc_dict["dialect_options"] = options
  4185. uniques[(schema, table_name)].append(uc_dict)
  4186. return uniques.items()
  4187. @reflection.cache
  4188. def get_table_comment(self, connection, table_name, schema=None, **kw):
  4189. data = self.get_multi_table_comment(
  4190. connection,
  4191. schema,
  4192. [table_name],
  4193. scope=ObjectScope.ANY,
  4194. kind=ObjectKind.ANY,
  4195. **kw,
  4196. )
  4197. return self._value_or_raise(data, table_name, schema)
  4198. @lru_cache()
  4199. def _comment_query(self, schema, has_filter_names, scope, kind):
  4200. relkinds = self._kind_to_relkinds(kind)
  4201. query = (
  4202. select(
  4203. pg_catalog.pg_class.c.relname,
  4204. pg_catalog.pg_description.c.description,
  4205. )
  4206. .select_from(pg_catalog.pg_class)
  4207. .outerjoin(
  4208. pg_catalog.pg_description,
  4209. sql.and_(
  4210. pg_catalog.pg_class.c.oid
  4211. == pg_catalog.pg_description.c.objoid,
  4212. pg_catalog.pg_description.c.objsubid == 0,
  4213. pg_catalog.pg_description.c.classoid
  4214. == sql.func.cast("pg_catalog.pg_class", REGCLASS),
  4215. ),
  4216. )
  4217. .where(self._pg_class_relkind_condition(relkinds))
  4218. )
  4219. query = self._pg_class_filter_scope_schema(query, schema, scope)
  4220. if has_filter_names:
  4221. query = query.where(
  4222. pg_catalog.pg_class.c.relname.in_(bindparam("filter_names"))
  4223. )
  4224. return query
  4225. def get_multi_table_comment(
  4226. self, connection, schema, filter_names, scope, kind, **kw
  4227. ):
  4228. has_filter_names, params = self._prepare_filter_names(filter_names)
  4229. query = self._comment_query(schema, has_filter_names, scope, kind)
  4230. result = connection.execute(query, params)
  4231. default = ReflectionDefaults.table_comment
  4232. return (
  4233. (
  4234. (schema, table),
  4235. {"text": comment} if comment is not None else default(),
  4236. )
  4237. for table, comment in result
  4238. )
  4239. @reflection.cache
  4240. def get_check_constraints(self, connection, table_name, schema=None, **kw):
  4241. data = self.get_multi_check_constraints(
  4242. connection,
  4243. schema,
  4244. [table_name],
  4245. scope=ObjectScope.ANY,
  4246. kind=ObjectKind.ANY,
  4247. **kw,
  4248. )
  4249. return self._value_or_raise(data, table_name, schema)
  4250. @lru_cache()
  4251. def _check_constraint_query(self, schema, has_filter_names, scope, kind):
  4252. relkinds = self._kind_to_relkinds(kind)
  4253. query = (
  4254. select(
  4255. pg_catalog.pg_class.c.relname,
  4256. pg_catalog.pg_constraint.c.conname,
  4257. # NOTE: avoid calling pg_get_constraintdef when not needed
  4258. # to speed up the query
  4259. sql.case(
  4260. (
  4261. pg_catalog.pg_constraint.c.oid.is_not(None),
  4262. pg_catalog.pg_get_constraintdef(
  4263. pg_catalog.pg_constraint.c.oid, True
  4264. ),
  4265. ),
  4266. else_=None,
  4267. ),
  4268. pg_catalog.pg_description.c.description,
  4269. )
  4270. .select_from(pg_catalog.pg_class)
  4271. .outerjoin(
  4272. pg_catalog.pg_constraint,
  4273. sql.and_(
  4274. pg_catalog.pg_class.c.oid
  4275. == pg_catalog.pg_constraint.c.conrelid,
  4276. pg_catalog.pg_constraint.c.contype == "c",
  4277. ),
  4278. )
  4279. .outerjoin(
  4280. pg_catalog.pg_description,
  4281. pg_catalog.pg_description.c.objoid
  4282. == pg_catalog.pg_constraint.c.oid,
  4283. )
  4284. .order_by(
  4285. pg_catalog.pg_class.c.relname,
  4286. pg_catalog.pg_constraint.c.conname,
  4287. )
  4288. .where(self._pg_class_relkind_condition(relkinds))
  4289. )
  4290. query = self._pg_class_filter_scope_schema(query, schema, scope)
  4291. if has_filter_names:
  4292. query = query.where(
  4293. pg_catalog.pg_class.c.relname.in_(bindparam("filter_names"))
  4294. )
  4295. return query
  4296. def get_multi_check_constraints(
  4297. self, connection, schema, filter_names, scope, kind, **kw
  4298. ):
  4299. has_filter_names, params = self._prepare_filter_names(filter_names)
  4300. query = self._check_constraint_query(
  4301. schema, has_filter_names, scope, kind
  4302. )
  4303. result = connection.execute(query, params)
  4304. check_constraints = defaultdict(list)
  4305. default = ReflectionDefaults.check_constraints
  4306. for table_name, check_name, src, comment in result:
  4307. # only two cases for check_name and src: both null or both defined
  4308. if check_name is None and src is None:
  4309. check_constraints[(schema, table_name)] = default()
  4310. continue
  4311. # samples:
  4312. # "CHECK (((a > 1) AND (a < 5)))"
  4313. # "CHECK (((a = 1) OR ((a > 2) AND (a < 5))))"
  4314. # "CHECK (((a > 1) AND (a < 5))) NOT VALID"
  4315. # "CHECK (some_boolean_function(a))"
  4316. # "CHECK (((a\n < 1)\n OR\n (a\n >= 5))\n)"
  4317. # "CHECK (a NOT NULL) NO INHERIT"
  4318. # "CHECK (a NOT NULL) NO INHERIT NOT VALID"
  4319. m = re.match(
  4320. r"^CHECK *\((.+)\)( NO INHERIT)?( NOT VALID)?$",
  4321. src,
  4322. flags=re.DOTALL,
  4323. )
  4324. if not m:
  4325. util.warn("Could not parse CHECK constraint text: %r" % src)
  4326. sqltext = ""
  4327. else:
  4328. sqltext = re.compile(
  4329. r"^[\s\n]*\((.+)\)[\s\n]*$", flags=re.DOTALL
  4330. ).sub(r"\1", m.group(1))
  4331. entry = {
  4332. "name": check_name,
  4333. "sqltext": sqltext,
  4334. "comment": comment,
  4335. }
  4336. if m:
  4337. do = {}
  4338. if " NOT VALID" in m.groups():
  4339. do["not_valid"] = True
  4340. if " NO INHERIT" in m.groups():
  4341. do["no_inherit"] = True
  4342. if do:
  4343. entry["dialect_options"] = do
  4344. check_constraints[(schema, table_name)].append(entry)
  4345. return check_constraints.items()
  4346. def _pg_type_filter_schema(self, query, schema):
  4347. if schema is None:
  4348. query = query.where(
  4349. pg_catalog.pg_type_is_visible(pg_catalog.pg_type.c.oid),
  4350. # ignore pg_catalog schema
  4351. pg_catalog.pg_namespace.c.nspname != "pg_catalog",
  4352. )
  4353. elif schema != "*":
  4354. query = query.where(pg_catalog.pg_namespace.c.nspname == schema)
  4355. return query
  4356. @lru_cache()
  4357. def _enum_query(self, schema):
  4358. lbl_agg_sq = (
  4359. select(
  4360. pg_catalog.pg_enum.c.enumtypid,
  4361. sql.func.array_agg(
  4362. aggregate_order_by(
  4363. # NOTE: cast since some postgresql derivatives may
  4364. # not support array_agg on the name type
  4365. pg_catalog.pg_enum.c.enumlabel.cast(TEXT),
  4366. pg_catalog.pg_enum.c.enumsortorder,
  4367. )
  4368. ).label("labels"),
  4369. )
  4370. .group_by(pg_catalog.pg_enum.c.enumtypid)
  4371. .subquery("lbl_agg")
  4372. )
  4373. query = (
  4374. select(
  4375. pg_catalog.pg_type.c.typname.label("name"),
  4376. pg_catalog.pg_type_is_visible(pg_catalog.pg_type.c.oid).label(
  4377. "visible"
  4378. ),
  4379. pg_catalog.pg_namespace.c.nspname.label("schema"),
  4380. lbl_agg_sq.c.labels.label("labels"),
  4381. )
  4382. .join(
  4383. pg_catalog.pg_namespace,
  4384. pg_catalog.pg_namespace.c.oid
  4385. == pg_catalog.pg_type.c.typnamespace,
  4386. )
  4387. .outerjoin(
  4388. lbl_agg_sq, pg_catalog.pg_type.c.oid == lbl_agg_sq.c.enumtypid
  4389. )
  4390. .where(pg_catalog.pg_type.c.typtype == "e")
  4391. .order_by(
  4392. pg_catalog.pg_namespace.c.nspname, pg_catalog.pg_type.c.typname
  4393. )
  4394. )
  4395. return self._pg_type_filter_schema(query, schema)
  4396. @reflection.cache
  4397. def _load_enums(self, connection, schema=None, **kw):
  4398. if not self.supports_native_enum:
  4399. return []
  4400. result = connection.execute(self._enum_query(schema))
  4401. enums = []
  4402. for name, visible, schema, labels in result:
  4403. enums.append(
  4404. {
  4405. "name": name,
  4406. "schema": schema,
  4407. "visible": visible,
  4408. "labels": [] if labels is None else labels,
  4409. }
  4410. )
  4411. return enums
  4412. @lru_cache()
  4413. def _domain_query(self, schema):
  4414. con_sq = (
  4415. select(
  4416. pg_catalog.pg_constraint.c.contypid,
  4417. sql.func.array_agg(
  4418. pg_catalog.pg_get_constraintdef(
  4419. pg_catalog.pg_constraint.c.oid, True
  4420. )
  4421. ).label("condefs"),
  4422. sql.func.array_agg(
  4423. # NOTE: cast since some postgresql derivatives may
  4424. # not support array_agg on the name type
  4425. pg_catalog.pg_constraint.c.conname.cast(TEXT)
  4426. ).label("connames"),
  4427. )
  4428. # The domain this constraint is on; zero if not a domain constraint
  4429. .where(pg_catalog.pg_constraint.c.contypid != 0)
  4430. .group_by(pg_catalog.pg_constraint.c.contypid)
  4431. .subquery("domain_constraints")
  4432. )
  4433. query = (
  4434. select(
  4435. pg_catalog.pg_type.c.typname.label("name"),
  4436. pg_catalog.format_type(
  4437. pg_catalog.pg_type.c.typbasetype,
  4438. pg_catalog.pg_type.c.typtypmod,
  4439. ).label("attype"),
  4440. (~pg_catalog.pg_type.c.typnotnull).label("nullable"),
  4441. pg_catalog.pg_type.c.typdefault.label("default"),
  4442. pg_catalog.pg_type_is_visible(pg_catalog.pg_type.c.oid).label(
  4443. "visible"
  4444. ),
  4445. pg_catalog.pg_namespace.c.nspname.label("schema"),
  4446. con_sq.c.condefs,
  4447. con_sq.c.connames,
  4448. pg_catalog.pg_collation.c.collname,
  4449. )
  4450. .join(
  4451. pg_catalog.pg_namespace,
  4452. pg_catalog.pg_namespace.c.oid
  4453. == pg_catalog.pg_type.c.typnamespace,
  4454. )
  4455. .outerjoin(
  4456. pg_catalog.pg_collation,
  4457. pg_catalog.pg_type.c.typcollation
  4458. == pg_catalog.pg_collation.c.oid,
  4459. )
  4460. .outerjoin(
  4461. con_sq,
  4462. pg_catalog.pg_type.c.oid == con_sq.c.contypid,
  4463. )
  4464. .where(pg_catalog.pg_type.c.typtype == "d")
  4465. .order_by(
  4466. pg_catalog.pg_namespace.c.nspname, pg_catalog.pg_type.c.typname
  4467. )
  4468. )
  4469. return self._pg_type_filter_schema(query, schema)
  4470. @reflection.cache
  4471. def _load_domains(self, connection, schema=None, **kw):
  4472. result = connection.execute(self._domain_query(schema))
  4473. domains: List[ReflectedDomain] = []
  4474. for domain in result.mappings():
  4475. # strip (30) from character varying(30)
  4476. attype = re.search(r"([^\(]+)", domain["attype"]).group(1)
  4477. constraints: List[ReflectedDomainConstraint] = []
  4478. if domain["connames"]:
  4479. # When a domain has multiple CHECK constraints, they will
  4480. # be tested in alphabetical order by name.
  4481. sorted_constraints = sorted(
  4482. zip(domain["connames"], domain["condefs"]),
  4483. key=lambda t: t[0],
  4484. )
  4485. for name, def_ in sorted_constraints:
  4486. # constraint is in the form "CHECK (expression)"
  4487. # or "NOT NULL". Ignore the "NOT NULL" and
  4488. # remove "CHECK (" and the tailing ")".
  4489. if def_.casefold().startswith("check"):
  4490. check = def_[7:-1]
  4491. constraints.append({"name": name, "check": check})
  4492. domain_rec: ReflectedDomain = {
  4493. "name": domain["name"],
  4494. "schema": domain["schema"],
  4495. "visible": domain["visible"],
  4496. "type": attype,
  4497. "nullable": domain["nullable"],
  4498. "default": domain["default"],
  4499. "constraints": constraints,
  4500. "collation": domain["collname"],
  4501. }
  4502. domains.append(domain_rec)
  4503. return domains
  4504. def _set_backslash_escapes(self, connection):
  4505. # this method is provided as an override hook for descendant
  4506. # dialects (e.g. Redshift), so removing it may break them
  4507. std_string = connection.exec_driver_sql(
  4508. "show standard_conforming_strings"
  4509. ).scalar()
  4510. self._backslash_escapes = std_string == "off"