base.py 172 KB

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