
    9                        S r SSKJr  SSKJr  SSKJr  SSKrSSKJr  SSKrSSK	J
r
  SSKJr  SSKrSS	KJr   " S
 S\5      r " S S\5      r " S S\5      r " S S\5      r " S S\5      r " S S\R,                  " \R.                  \5      5      rS r " S S\5      r " S S\5      r " S S\5      r " S S\5      rS rg) a  Provides helper methods for dealing with Cloud Spanner Writes API.

The main reasons for adding the util functions for Writes API are as below:
  - API expects column values to be extra_types.JsonValue, apitool cannot
    handle it by default.
  - for different data types the API expects different formats, for example:
    for INT64, API expects a string value; for FLOAT64, it expects a number.
    As the values user input are strings by default, the type conversion is
    necessary.
    )absolute_import)division)unicode_literalsN)OrderedDict)extra_types)Error)zipc                       \ rS rSrSrSrg)BadColumnNameError'   zDRaised when a column name entered by user is not found in the table. N__name__
__module____qualname____firstlineno____doc____static_attributes__r       4lib/googlecloudsdk/command_lib/spanner/write_util.pyr   r   '   s    Lr   r   c                       \ rS rSrSrSrg)BadTableNameError+   zFRaised when a table name entered by user is not found in the database.r   Nr   r   r   r   r   r   +   s    Nr   r   c                       \ rS rSrSrSrg)InvalidKeysError/   zARaised when the number of keys user input does not match the DDL.r   Nr   r   r   r   r   r   /   s    Ir   r   c                       \ rS rSrSrSrg)InvalidArrayInputError3   zGRaised when the user tries to input a list as a value in the data flag.r   Nr   r   r   r   r   r   3   s    Or   r   c                       \ rS rSrSr\R                  " S\R                  \R                  -  5      r	S r
S r\S 5       rS rSrg	)
_TableColumn7   zA wrapper that stores the column information.

Attributes:
  name: String, the name of the table column.
  col_type: _ScalarColumnType or _ArrayColumnType.
a  
            # A column definition has a name and a type, with some additional
            # properties.
            # Some examples:
            #    Foo INT64 NOT NULL
            #    Bar STRING(1024)
            #    Baz ARRAY<FLOAT32>
            [`]?(?P<name>\w+)[`]?\s+
            (?P<type>[\w<>]+)
            # We don't care about "NOT NULL", and the length number after STRING
            # or BYTES (e.g.STRING(MAX), BYTES(1024)).
        c                     Xl         X l        g Nnamecol_type)selfr&   r'   s      r   __init___TableColumn.__init__L   s    IMr   c                 t    U R                   UR                   :H  =(       a    U R                  UR                  :H  $ r$   r%   r(   others     r   __eq___TableColumn.__eq__P   s'    99

"Ft}}'FFr   c                     U R                   R                  U5      nU(       d  [        SR                  U5      5      eUR	                  S5      n[
        R                  UR	                  S5      5      n[        X45      $ )a|  Constructs an instance of _TableColumn from a column_def DDL statement.

Args:
  column_ddl: string, the parsed string contains the column name and type
    information. Example: SingerId INT64 NOT NULL.

Returns:
  A _TableColumn object.

Raises:
  ValueError: invalid DDL, this error shouldn't happen in theory, as the API
    is expected to return valid DDL statement strings.
zInvalid DDL: [{}].r&   type)_COLUMN_DDL_PATTERNsearch
ValueErrorformatgroup_ColumnTypeFromDdlr!   )cls
column_ddlcolumn_matchcolumn_namer'   s        r   r8   _TableColumn.FromDdlS   sj     **11*=L+22:>??$$V,K""<#5#5f#=>H..r   c                 8    U R                   R                  U5      $ )a  Convert the user input values to JSON value or JSON array value.

Args:
  value: String or string list, the user input values of the column.

Returns:
  extra_types.JsonArray or extra_types.JsonValue, the json value of a single
      column in the format that API accepts.
)r'   GetJsonValuer(   values     r   GetJsonValues_TableColumn.GetJsonValuesj   s     ==%%e,,r   )r'   r&   N)r   r   r   r   r   recompileDOTALLVERBOSEr2   r)   r.   classmethodr8   rB   r   r   r   r   r!   r!   7   sS     

 YY#%G / /,
-r   r!   c                   V    \ rS rSrSrSrS r\S 5       r\	R                  S 5       rSrg)	r7   w   aR  A wrapper that stores the column type information.

A column type can be one of the scalar types such as integers, as well as
    array. An array type is an ordered list of zero or more elements of
    scalar type.

Attributes:
  scalar_type: String, the type of the column or the element of the column
      (if the column is an array).
)BOOLBYTESDATEFLOAT64INT64STRING	TIMESTAMPNUMERICJSON	TOKENLISTFLOAT32c                     Xl         g r$   scalar_type)r(   rX   s     r   r)   _ColumnType.__init__   s    "r   c                     SnU R                    H  nX1;   d  M
  Un  O   U(       d  [        SR                  U5      5      eUR                  S5      (       a  [	        U5      $ [        U5      $ )a  Constructs a _ColumnType object from a partial DDL statement.

Args:
  column_type_ddl: string, the parsed string only contains the column type
    information. Example: INT64 NOT NULL, ARRAY<STRING(MAX)> or BYTES(200).

Returns:
  A _ArrayColumnType or a _ScalarColumnType object.

Raises:
  ValueError: invalid DDL, this error shouldn't happen in theory, as the API
    is expected to return valid DDL statement strings.
Nz$Invalid DDL: unrecognized type [{}].ARRAY)_SCALAR_TYPESr4   r5   
startswith_ArrayColumnType_ScalarColumnType)r9   column_type_ddlscalar_match	data_types       r   r8   _ColumnType.FromDdl   sr     L&&			%  '
 
0
7
7
HJ J !!'**l++|,,r   c                     [        5       er$   )NotImplementedErrorr@   s     r   r?   _ColumnType.GetJsonValue   s    

r   rW   N)r   r   r   r   r   r\   r)   rH   r8   abcabstractmethodr?   r   r   r   r   r7   r7   w   sB    	K-# - -:    r   r7   c                 2   US:X  a  [         R                  " SS9$ U S:X  a'  UR                  5       S:H  n[         R                  " US9$ U S;   a7  US;   a  [         R                  " US	9$ [         R                  " [        U5      S
9$ [         R                  " US	9$ )a  Convert the user input scalar value to JSON value.

Args:
  scalar_type: String, the scalar type of the column, e.g INT64, DATE.
  scalar_value: String, the value of the column that user inputs.

Returns:
  An API accepts JSON value of a column or an element of an array column.
NULLT)is_nullrK   TRUE)boolean_value)rN   rU   )NaNInfinityz	-Infinity)string_value)double_value)r   	JsonValueupperfloat)rX   scalar_value
bool_values      r   ConvertJsonValueForScalarTypesrw      s     V  ..f##%/J  z::,,77""==""l0CDD
   l;;r   c                   4   ^  \ rS rSrU 4S jrS rS rSrU =r$ )r_      c                 ,   > [         [        U ]  U5        g r$   )superr_   r)   r(   rX   	__class__s     r   r)   _ScalarColumnType.__init__   s    	
T+K8r   c                 b    U R                   UR                   :H  =(       a    [        U[        5      $ r$   )rX   
isinstancer_   r,   s     r   r.   _ScalarColumnType.__eq__   s-    u000 "Z 6" "r   c                 .    [        U R                  U5      $ r$   )rw   rX   r@   s     r   r?   _ScalarColumnType.GetJsonValue   s    )$*:*:EBBr   r   	r   r   r   r   r)   r.   r?   r   __classcell__r}   s   @r   r_   r_      s    9"C Cr   r_   c                   4   ^  \ rS rSrU 4S jrS rS rSrU =r$ )r^      c                 ,   > [         [        U ]  U5        g r$   )r{   r^   r)   r|   s     r   r)   _ArrayColumnType.__init__   s    	
D*;7r   c                 b    U R                   UR                   :H  =(       a    [        U[        5      $ r$   )rX   r   r^   r,   s     r   r.   _ArrayColumnType.__eq__   s-    u000 !Z6! !r   c                     [         R                  " [         R                  " U Vs/ s H  n[        U R                  U5      PM     snS9S9$ s  snf )N)entries)array_value)r   rr   	JsonArrayrw   rX   )r(   valuesvs      r   r?   _ArrayColumnType.GetJsonValue   sM      ))IO3
IOA*4+;+;Q?3
  3
s    Ar   r   r   s   @r   r^   r^      s    8! r   r^   c                       \ rS rSrSrS rSrg)ColumnJsonData   a   Container for the column name and value to be written in a table.

Attributes:
  col_name: String, the name of the column to be written.
  col_value: extra_types.JsonArray(array column) or
    extra_types.JsonValue(scalar column), the value to be written.
c                     Xl         X l        g r$   col_name	col_value)r(   r   r   s      r   r)   ColumnJsonData.__init__   s    MNr   r   N)r   r   r   r   r   r)   r   r   r   r   r   r      s    r   r   c                       \ rS rSrSr\R                  " S\R                  \R                  -  5      r	SS jr
S rS r\S 5       rS	 rS
 rSrg)Table   aI  Container for the properties of a table in Cloud Spanner database.

Attributes:
  name: String, the name of table.
  _columns: OrderedDict, with keys are the column names and values are the
    _TableColumn objects.
  _primary_keys: String list, the names of the primary key columns in the
    order defined in the DDL statement
a  
          # Every table starts with "CREATE TABLE" followed by name and column
          # definitions, in a big set of parenthesis.
          # For example:
          #    CREATE TABLE Foos (
          #        Bar INT64 NOT NULL,
          #        Baz INT64 NOT NULL,
          #        Qux STRING(MAX),
          #    )
          CREATE\s+TABLE\s+
          (?P<name>[\w\.]+)\s+\(\s+
          (?P<columns>.*)\)\s+
          # Then, it has "PRIMARY KEY" and a list of primary keys, in parens:
          # PRIMARY KEY ( Bar, Qux )
          PRIMARY\s+KEY\s*\(
          (?P<primary_keys>.*)\)
          # It may have extra instructions on the end (e.g. INTERLEAVE) to
          # tell Spanner how to store the data, but we don't really care.
      Nc                 <    Xl         X l        U=(       d    / U l        g r$   )r&   _columns_primary_keys)r(   
table_namer   r   s       r   r)   Table.__init__  s    IM&,"Dr   c                     / n[         R                  " U5       HA  u  p4U R                  U5      nUR                  U5      nUR	                  [        X65      5        MC     U$ )aA  Get the column names and values to be written from data input.

Args:
  data_dict: Dictionary where keys are the column names and values are user
      input data value, which is parsed from --data argument in the command.

Returns:
  List of ColumnJsonData, which includes the column names and values to be
    written.
)six	iteritems_FindColumnByNamerB   appendr   )r(   	data_dictcolumn_listr   r   col_in_tablecol_json_values          r   GetJsonDataTable.GetJsonData  sZ     K"}}Y7++H5l#11)<nAB  8
 r   c           	      v   [        U5      [        U R                  5      :w  aB  [        SR                  [        U R                  5      U R                  [        U5      5      5      e/ n[        U R                  U5       H8  u  p4U R                  U5      nUR                  U5      nUR                  U5        M:     U$ )zGet the primary key values to be written from keys input.

Args:
  keys_list: String list, the primary key values of the row to be deleted.

Returns:
  List of extra_types.JsonValue.

Raises:
  InvalidKeysError: the keys are invalid.
zOInvalid keys. There are {} primary key columns in the table [{}]. {} are given.)	lenr   r   r5   r&   r	   r   rB   r   )r(   	keys_listkeys_json_listkey_name	key_valuer   r   s          r   GetJsonKeysTable.GetJsonKeys.  s     9~T//00 &$$$%tyy#i.BC C
 N"4#5#5yA++H5l#11)<nN+  B
 r   c                    / nU GH  nU R                   R                  U5      nU(       d  M(  UR                  S5      nXb:w  a  UR                  U5        MQ  UR                  S5      n[	        5       nUR                  S5       HF  n	U	(       d  M  U	R                  5       (       a  M#  [        R                  U	5      n
XU
R                  '   MH     UR                  5       S   nUR                  S5       Vs/ s H  oR                  5       PM     nn[        X(U5      s  $    [        SR                  USR                  U5      5      5      es  snf )a   Constructs a Table from ddl statements.

Args:
  database_ddl: String list, the ddl statements of the current table from
      server.
  table_name: String, the table name user inputs.

Returns:
  Table.

Raises:
  BadTableNameError: the table name is invalid.
  ValueError: Invalid Ddl.
r&   columns,primary_keysz4Table name [{}] is invalid. Valid table names: [{}]., )_TABLE_DDL_PATTERNr3   r6   r   r   splitisspacer!   r8   r&   	groupdictstripr   r   r5   join)r9   database_ddlr   table_name_listddltable_matchr&   column_defscolumn_dictr:   columnraw_primary_keyskprimary_keys_lists                 r   r8   Table.FromDdlL  s.   " O **11#6kv&d		t$%%i0kMk#))#.*:j0022''
3&%+fkk
"	 / %..0@.>.D.DS.IJ.I779.IJ:,=>>5 8 >EE		/2	45 5	 Ks   2Ec                     [        5       n[        R                  " U R                  5       H  u  p#UR                  X'   M     U$ )z[Maps the column name to the column type.

Returns:
  OrderedDict of column names to types.
)r   r   r   r   r'   )r(   col_to_typer&   r   s       r   GetColumnTypesTable.GetColumnTypes  s6     -Kdmm4 //k 5r   c                      U R                   U   $ ! [         aM    SR                  [        U R                   R	                  5       5      5      n[        SR                  X5      5      ef = f)zFind the _TableColumn object with the given column name.

Args:
  col_name: String, the name of the column.

Returns:
  _TableColumn.

Raises:
  BadColumnNameError: the column name is invalid.
r   z6Column name [{}] is invalid. Valid column names: [{}].)r   KeyErrorr   listkeysr   r5   )r(   r   valid_column_namess      r   r   Table._FindColumnByName  sd    -]]8$$ -99T$--*<*<*>%?@
B
I
I,- --s
    AA()r   r   r&   r$   )r   r   r   r   r   rD   rE   rF   rG   r   r)   r   r   rH   r8   r   r   r   r   r   r   r   r      s]     zz
$ 99rzz!'#*-
(< 05 05d	-r   r   c                     U R                  5       n[        R                  " U5       HM  u  p4X#   n[        U[        5      (       d  M   [        U[
        5      SL d  M5  [        SR                  X45      5      e   U$ )a  Checks array input is valid.

Args:
  table: Table, the table which data is being modified.
  data: OrderedDict, the data entered by the user.

Returns:
  data (OrderedDict) the validated data.

Raises:
  InvalidArrayInputError: if the input contains an array which is invalid.
FzgColumn name [{}] has an invalid array input: {}. `--flags-file` should be used to specify array values.)r   r   r   r   r^   r   r   r5   )tabledatar   r   rA   r'   s         r   ValidateArrayInputr     su     $$&+}}T*mf"H("$ $(25$(?5(H"44:F64IK K	 + 
+r   )r   
__future__r   r   r   rg   collectionsr   rD   apitools.base.pyr   googlecloudsdk.core.exceptionsr   r   	six.movesr	   r   r   r   r   objectr!   with_metaclassABCMetar7   rw   r_   r^   r   r   r   r   r   r   <module>r      s   	 '  ' 
 # 	 ( 0 
 M MO OJu JPU P=-6 =-@4 #$$S[[&9 4 n<:
C 
C{  V f-F f-Rr   